diff options
1234 files changed, 32832 insertions, 10177 deletions
diff --git a/AconfigFlags.bp b/AconfigFlags.bp index 497619ae0613..6e37b7e55eef 100644 --- a/AconfigFlags.bp +++ b/AconfigFlags.bp @@ -21,6 +21,7 @@ aconfig_declarations_group { java_aconfig_libraries: [ // !!! KEEP THIS LIST ALPHABETICAL !!! "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", @@ -71,6 +72,7 @@ aconfig_declarations_group { "android.service.dreams.flags-aconfig-java", "android.service.notification.flags-aconfig-java", "android.service.appprediction.flags-aconfig-java", + "android.service.quickaccesswallet.flags-aconfig-java", "android.service.voice.flags-aconfig-java", "android.speech.flags-aconfig-java", "android.systemserver.flags-aconfig-java", @@ -1757,3 +1759,34 @@ cc_aconfig_library { ], min_sdk_version: "apex_inherit", } + +// Settings Lib +aconfig_declarations { + name: "aconfig_settingslib_flags", + package: "com.android.settingslib.flags", + container: "system", + srcs: [ + "packages/SettingsLib/aconfig/settingslib.aconfig", + ], +} + +java_aconfig_library { + name: "aconfig_settingslib_flags_java_lib", + aconfig_declarations: "aconfig_settingslib_flags", + defaults: ["framework-minus-apex-aconfig-java-defaults"], +} + +// Quick Access Wallet +aconfig_declarations { + name: "android.service.quickaccesswallet.flags-aconfig", + package: "android.service.quickaccesswallet", + exportable: true, + container: "system", + srcs: ["core/java/android/service/quickaccesswallet/flags.aconfig"], +} + +java_aconfig_library { + name: "android.service.quickaccesswallet.flags-aconfig-java", + aconfig_declarations: "android.service.quickaccesswallet.flags-aconfig", + defaults: ["framework-minus-apex-aconfig-java-defaults"], +} diff --git a/Android.bp b/Android.bp index 26d0d65f329c..48f0928f24d7 100644 --- a/Android.bp +++ b/Android.bp @@ -220,7 +220,7 @@ java_library { "android.hardware.contexthub-V1.0-java", "android.hardware.contexthub-V1.1-java", "android.hardware.contexthub-V1.2-java", - "android.hardware.contexthub-V3-java", + "android.hardware.contexthub-V4-java", "android.hardware.gnss-V1.0-java", "android.hardware.gnss-V2.1-java", "android.hardware.health-V1.0-java-constants", @@ -399,6 +399,7 @@ java_defaults { "com.android.sysprop.foldlockbehavior", "com.android.sysprop.view", "framework-internal-utils", + "dynamic_instrumentation_manager_aidl-java", // If MimeMap ever becomes its own APEX, then this dependency would need to be removed // in favor of an API stubs dependency in java_library "framework" below. "mimemap", diff --git a/apex/jobscheduler/framework/java/android/app/JobSchedulerImpl.java b/apex/jobscheduler/framework/java/android/app/JobSchedulerImpl.java index fb5ef8771c26..e9b11f46ddde 100644 --- a/apex/jobscheduler/framework/java/android/app/JobSchedulerImpl.java +++ b/apex/jobscheduler/framework/java/android/app/JobSchedulerImpl.java @@ -25,11 +25,13 @@ import android.app.job.JobInfo; import android.app.job.JobScheduler; import android.app.job.JobSnapshot; import android.app.job.JobWorkItem; +import android.app.job.PendingJobReasonsInfo; import android.content.Context; import android.content.pm.ParceledListSlice; import android.os.RemoteException; import android.util.ArrayMap; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; @@ -183,6 +185,16 @@ public class JobSchedulerImpl extends JobScheduler { } @Override + @NonNull + public List<PendingJobReasonsInfo> getPendingJobReasonsHistory(int jobId) { + try { + return mBinder.getPendingJobReasonsHistory(mNamespace, jobId); + } catch (RemoteException e) { + return Collections.EMPTY_LIST; + } + } + + @Override public boolean canRunUserInitiatedJobs() { try { return mBinder.canRunUserInitiatedJobs(mContext.getOpPackageName()); diff --git a/apex/jobscheduler/framework/java/android/app/job/IJobScheduler.aidl b/apex/jobscheduler/framework/java/android/app/job/IJobScheduler.aidl index 21051b520d84..dc7f3d143e4c 100644 --- a/apex/jobscheduler/framework/java/android/app/job/IJobScheduler.aidl +++ b/apex/jobscheduler/framework/java/android/app/job/IJobScheduler.aidl @@ -20,6 +20,7 @@ import android.app.job.IUserVisibleJobObserver; import android.app.job.JobInfo; import android.app.job.JobSnapshot; import android.app.job.JobWorkItem; +import android.app.job.PendingJobReasonsInfo; import android.content.pm.ParceledListSlice; import java.util.Map; @@ -40,6 +41,7 @@ interface IJobScheduler { JobInfo getPendingJob(String namespace, int jobId); int getPendingJobReason(String namespace, int jobId); int[] getPendingJobReasons(String namespace, int jobId); + List<PendingJobReasonsInfo> getPendingJobReasonsHistory(String namespace, int jobId); boolean canRunUserInitiatedJobs(String packageName); boolean hasRunUserInitiatedJobsPermission(String packageName, int userId); List<JobInfo> getStartedJobs(); diff --git a/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java b/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java index bfdd15e9b0cd..4fbd55a5d528 100644 --- a/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java +++ b/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java @@ -493,6 +493,34 @@ public abstract class JobScheduler { } /** + * For the given {@code jobId}, returns a limited historical view of why the job may have + * been pending execution. The returned list is composed of {@link PendingJobReasonsInfo} + * objects, each of which include a timestamp since epoch along with an array of + * unsatisfied constraints represented by {@link PendingJobReason PendingJobReason constants}. + * <p> + * These constants could either be explicitly set constraints on the job or implicit + * constraints imposed by the system due to various reasons. + * The results can be used to debug why a given job may have been pending execution. + * <p> + * If the only {@link PendingJobReason} for the timestamp is + * {@link PendingJobReason#PENDING_JOB_REASON_UNDEFINED}, it could mean that + * the job was ready to be executed at that point in time. + * <p> + * Note: there is no set interval for the timestamps in the returned list since + * constraint changes occur based on device status and various other factors. + * <p> + * Note: the pending job reasons history is not persisted across device reboots. + * <p> + * @throws IllegalArgumentException if the {@code jobId} is invalid. + * @see #getPendingJobReasons(int) + */ + @FlaggedApi(Flags.FLAG_GET_PENDING_JOB_REASONS_HISTORY_API) + @NonNull + public List<PendingJobReasonsInfo> getPendingJobReasonsHistory(int jobId) { + throw new UnsupportedOperationException("Not implemented by " + getClass()); + } + + /** * Returns {@code true} if the calling app currently holds the * {@link android.Manifest.permission#RUN_USER_INITIATED_JOBS} permission, allowing it to run * user-initiated jobs. diff --git a/apex/jobscheduler/framework/java/android/app/job/PendingJobReasonsInfo.aidl b/apex/jobscheduler/framework/java/android/app/job/PendingJobReasonsInfo.aidl new file mode 100644 index 000000000000..1a027020e25f --- /dev/null +++ b/apex/jobscheduler/framework/java/android/app/job/PendingJobReasonsInfo.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.job; + + parcelable PendingJobReasonsInfo; diff --git a/apex/jobscheduler/framework/java/android/app/job/PendingJobReasonsInfo.java b/apex/jobscheduler/framework/java/android/app/job/PendingJobReasonsInfo.java new file mode 100644 index 000000000000..3c96bab80794 --- /dev/null +++ b/apex/jobscheduler/framework/java/android/app/job/PendingJobReasonsInfo.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.job; + +import android.annotation.CurrentTimeMillisLong; +import android.annotation.FlaggedApi; +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * A simple wrapper which includes a timestamp (in millis since epoch) + * and an array of {@link JobScheduler.PendingJobReason reasons} at that timestamp + * for why a particular job may be pending. + */ +@FlaggedApi(Flags.FLAG_GET_PENDING_JOB_REASONS_HISTORY_API) +public final class PendingJobReasonsInfo implements Parcelable { + + @CurrentTimeMillisLong + private final long mTimestampMillis; + + @NonNull + @JobScheduler.PendingJobReason + private final int[] mPendingJobReasons; + + public PendingJobReasonsInfo(long timestampMillis, + @NonNull @JobScheduler.PendingJobReason int[] reasons) { + mTimestampMillis = timestampMillis; + mPendingJobReasons = reasons; + } + + /** + * @return the time (in millis since epoch) associated with the set of pending job reasons. + */ + @CurrentTimeMillisLong + public long getTimestampMillis() { + return mTimestampMillis; + } + + /** + * Returns a set of {@link android.app.job.JobScheduler.PendingJobReason reasons} representing + * why the job may not have executed at the associated timestamp. + * <p> + * These reasons could either be explicitly set constraints on the job or implicit + * constraints imposed by the system due to various reasons. + * <p> + * Note: if the only {@link android.app.job.JobScheduler.PendingJobReason} present is + * {@link JobScheduler.PendingJobReason#PENDING_JOB_REASON_UNDEFINED}, it could mean + * that the job was ready to be executed at that time. + */ + @NonNull + @JobScheduler.PendingJobReason + public int[] getPendingJobReasons() { + return mPendingJobReasons; + } + + private PendingJobReasonsInfo(Parcel in) { + mTimestampMillis = in.readLong(); + mPendingJobReasons = in.createIntArray(); + } + + @NonNull + public static final Creator<PendingJobReasonsInfo> CREATOR = + new Creator<>() { + @Override + public PendingJobReasonsInfo createFromParcel(Parcel in) { + return new PendingJobReasonsInfo(in); + } + + @Override + public PendingJobReasonsInfo[] newArray(int size) { + return new PendingJobReasonsInfo[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeLong(mTimestampMillis); + dest.writeIntArray(mPendingJobReasons); + } +} 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 f569388ef3c1..1c6e40e25a92 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java @@ -44,6 +44,7 @@ import android.app.job.JobScheduler; import android.app.job.JobService; import android.app.job.JobSnapshot; import android.app.job.JobWorkItem; +import android.app.job.PendingJobReasonsInfo; import android.app.job.UserVisibleJobSummary; import android.app.usage.UsageStatsManager; import android.app.usage.UsageStatsManagerInternal; @@ -2140,6 +2141,20 @@ public class JobSchedulerService extends com.android.server.SystemService return new int[] { JobScheduler.PENDING_JOB_REASON_UNDEFINED }; } + @NonNull + private List<PendingJobReasonsInfo> getPendingJobReasonsHistory( + int uid, String namespace, int jobId) { + synchronized (mLock) { + final JobStatus job = mJobs.getJobByUidAndJobId(uid, namespace, jobId); + if (job == null) { + // Job doesn't exist. + throw new IllegalArgumentException("Invalid job id"); + } + + return job.getPendingJobReasonsHistory(); + } + } + private JobInfo getPendingJob(int uid, @Nullable String namespace, int jobId) { synchronized (mLock) { ArraySet<JobStatus> jobs = mJobs.getJobsByUid(uid); @@ -5122,6 +5137,19 @@ public class JobSchedulerService extends com.android.server.SystemService } @Override + public List<PendingJobReasonsInfo> getPendingJobReasonsHistory(String namespace, int jobId) + throws RemoteException { + final int uid = Binder.getCallingUid(); + final long ident = Binder.clearCallingIdentity(); + try { + return JobSchedulerService.this.getPendingJobReasonsHistory( + uid, validateNamespace(namespace), jobId); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + @Override public void cancelAll() throws RemoteException { final int uid = Binder.getCallingUid(); final long ident = Binder.clearCallingIdentity(); @@ -5857,6 +5885,9 @@ public class JobSchedulerService extends com.android.server.SystemService pw.print(android.app.job.Flags.FLAG_GET_PENDING_JOB_REASONS_API, android.app.job.Flags.getPendingJobReasonsApi()); pw.println(); + pw.print(android.app.job.Flags.FLAG_GET_PENDING_JOB_REASONS_HISTORY_API, + android.app.job.Flags.getPendingJobReasonsHistoryApi()); + pw.println(); pw.decreaseIndent(); pw.println(); diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java index a4a302450849..f3bc9c747f17 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java @@ -442,6 +442,9 @@ public final class JobSchedulerShellCommand extends BasicShellCommandHandler { case android.app.job.Flags.FLAG_GET_PENDING_JOB_REASONS_API: pw.println(android.app.job.Flags.getPendingJobReasonsApi()); break; + case android.app.job.Flags.FLAG_GET_PENDING_JOB_REASONS_HISTORY_API: + pw.println(android.app.job.Flags.getPendingJobReasonsHistoryApi()); + break; default: pw.println("Unknown flag: " + flagName); break; 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 58579eb0db47..b0784f1c69fd 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 @@ -32,6 +32,7 @@ import android.app.job.JobInfo; import android.app.job.JobParameters; import android.app.job.JobScheduler; import android.app.job.JobWorkItem; +import android.app.job.PendingJobReasonsInfo; import android.app.job.UserVisibleJobSummary; import android.content.ClipData; import android.content.ComponentName; @@ -39,6 +40,7 @@ import android.net.Network; import android.net.NetworkRequest; import android.net.Uri; import android.os.RemoteException; +import android.os.SystemClock; import android.os.UserHandle; import android.provider.MediaStore; import android.text.format.DateFormat; @@ -72,6 +74,7 @@ import java.security.MessageDigest; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.List; import java.util.Objects; import java.util.Random; import java.util.function.Predicate; @@ -515,6 +518,10 @@ public final class JobStatus { private final long[] mConstraintUpdatedTimesElapsed = new long[NUM_CONSTRAINT_CHANGE_HISTORY]; private final int[] mConstraintStatusHistory = new int[NUM_CONSTRAINT_CHANGE_HISTORY]; + private final List<PendingJobReasonsInfo> mPendingJobReasonsHistory = new ArrayList<>(); + private static final int PENDING_JOB_HISTORY_RETURN_LIMIT = 10; + private static final int PENDING_JOB_HISTORY_TRIM_THRESHOLD = 25; + /** * For use only by ContentObserverController: state it is maintaining about content URIs * being observed. @@ -1992,6 +1999,16 @@ public final class JobStatus { mReasonReadyToUnready = JobParameters.STOP_REASON_UNDEFINED; } + final int unsatisfiedConstraints = ~satisfiedConstraints + & (requiredConstraints | mDynamicConstraints | IMPLICIT_CONSTRAINTS); + populatePendingJobReasonsHistoryMap(isReady, nowElapsed, unsatisfiedConstraints); + final int historySize = mPendingJobReasonsHistory.size(); + if (historySize >= PENDING_JOB_HISTORY_TRIM_THRESHOLD) { + // Ensure trimming doesn't occur too often - max history we currently return is 10 + mPendingJobReasonsHistory.subList(0, historySize - PENDING_JOB_HISTORY_RETURN_LIMIT) + .clear(); + } + return true; } @@ -2066,14 +2083,10 @@ public final class JobStatus { } } - /** - * This will return all potential reasons why the job is pending. - */ @NonNull - public int[] getPendingJobReasons() { + public ArrayList<Integer> constraintsToPendingJobReasons(int unsatisfiedConstraints) { final ArrayList<Integer> reasons = new ArrayList<>(); - final int unsatisfiedConstraints = ~satisfiedConstraints - & (requiredConstraints | mDynamicConstraints | IMPLICIT_CONSTRAINTS); + if ((CONSTRAINT_BACKGROUND_NOT_RESTRICTED & unsatisfiedConstraints) != 0) { // The BACKGROUND_NOT_RESTRICTED constraint could be unsatisfied either because // the app is background restricted, or because we're restricting background work @@ -2159,6 +2172,18 @@ public final class JobStatus { } } + return reasons; + } + + /** + * This will return all potential reasons why the job is pending. + */ + @NonNull + public int[] getPendingJobReasons() { + final int unsatisfiedConstraints = ~satisfiedConstraints + & (requiredConstraints | mDynamicConstraints | IMPLICIT_CONSTRAINTS); + final ArrayList<Integer> reasons = constraintsToPendingJobReasons(unsatisfiedConstraints); + if (reasons.isEmpty()) { if (getEffectiveStandbyBucket() == NEVER_INDEX) { Slog.wtf(TAG, "App in NEVER bucket querying pending job reason"); @@ -2178,6 +2203,55 @@ public final class JobStatus { return reasonsArr; } + private void populatePendingJobReasonsHistoryMap(boolean isReady, + long constraintTimestamp, int unsatisfiedConstraints) { + final long constraintTimestampEpoch = // system_boot_time + constraint_satisfied_time + (System.currentTimeMillis() - SystemClock.elapsedRealtime()) + constraintTimestamp; + + if (isReady) { + // Job is ready to execute. At this point, if the job doesn't execute, it might be + // because of the app itself; if not, note it as undefined (documented in javadoc). + mPendingJobReasonsHistory.addLast( + new PendingJobReasonsInfo( + constraintTimestampEpoch, + new int[] { serviceProcessName != null + ? JobScheduler.PENDING_JOB_REASON_APP + : JobScheduler.PENDING_JOB_REASON_UNDEFINED })); + return; + } + + final ArrayList<Integer> reasons = constraintsToPendingJobReasons(unsatisfiedConstraints); + if (reasons.isEmpty()) { + // If the job is not waiting on any constraints to be met, note it as undefined. + reasons.add(JobScheduler.PENDING_JOB_REASON_UNDEFINED); + } + + final int[] reasonsArr = new int[reasons.size()]; + for (int i = 0; i < reasonsArr.length; i++) { + reasonsArr[i] = reasons.get(i); + } + mPendingJobReasonsHistory.addLast( + new PendingJobReasonsInfo(constraintTimestampEpoch, reasonsArr)); + } + + /** + * Returns the last {@link #PENDING_JOB_HISTORY_RETURN_LIMIT} constraint changes. + */ + @NonNull + public List<PendingJobReasonsInfo> getPendingJobReasonsHistory() { + final List<PendingJobReasonsInfo> returnList = + new ArrayList<>(PENDING_JOB_HISTORY_RETURN_LIMIT); + final int historySize = mPendingJobReasonsHistory.size(); + if (historySize != 0) { + returnList.addAll( + mPendingJobReasonsHistory.subList( + Math.max(0, historySize - PENDING_JOB_HISTORY_RETURN_LIMIT), + historySize)); + } + + return returnList; + } + /** @return whether or not the @param constraint is satisfied */ public boolean isConstraintSatisfied(int constraint) { return (satisfiedConstraints&constraint) != 0; diff --git a/api/Android.bp b/api/Android.bp index 0ac85e28de1a..73262030ee37 100644 --- a/api/Android.bp +++ b/api/Android.bp @@ -127,27 +127,54 @@ combined_apis { }), } +// Create a single file containing the latest released version of the whole +// Android public API. +java_genrule { + name: "android.api.merged.public.latest", + srcs: [ + ":android.api.combined.public.latest", + ], + out: ["public-latest.txt"], + tools: ["metalava"], + cmd: metalava_cmd + " merge-signatures --format=2.0 $(in) --out $(out)", +} + +// Make sure that the Android public API is compatible with the +// previously released public API. java_genrule { name: "frameworks-base-api-current-compat", srcs: [ - ":android.api.public.latest", + ":android.api.merged.public.latest", ":android-incompatibilities.api.public.latest", ":frameworks-base-api-current.txt", ], out: ["updated-baseline.txt"], tools: ["metalava"], cmd: metalava_cmd + - "--check-compatibility:api:released $(location :android.api.public.latest) " + + "--check-compatibility:api:released $(location :android.api.merged.public.latest) " + "--baseline:compatibility:released $(location :android-incompatibilities.api.public.latest) " + "--update-baseline:compatibility:released $(genDir)/updated-baseline.txt " + "$(location :frameworks-base-api-current.txt)", } +// Create a single file containing the latest released version of the whole +// Android system API. +java_genrule { + name: "android.api.merged.system.latest", + srcs: [ + ":android.api.combined.system.latest", + ], + out: ["system-latest.txt"], + tools: ["metalava"], + cmd: metalava_cmd + " merge-signatures --format=2.0 $(in) --out $(out)", +} + +// Make sure that the Android system API is compatible with the +// previously released system API. java_genrule { name: "frameworks-base-api-system-current-compat", srcs: [ - ":android.api.public.latest", - ":android.api.system.latest", + ":android.api.merged.system.latest", ":android-incompatibilities.api.system.latest", ":frameworks-base-api-current.txt", ":frameworks-base-api-system-current.txt", @@ -155,20 +182,31 @@ java_genrule { out: ["updated-baseline.txt"], tools: ["metalava"], cmd: metalava_cmd + - "--check-compatibility:api:released $(location :android.api.public.latest) " + - "--check-compatibility:api:released $(location :android.api.system.latest) " + + "--check-compatibility:api:released $(location :android.api.merged.system.latest) " + "--baseline:compatibility:released $(location :android-incompatibilities.api.system.latest) " + "--update-baseline:compatibility:released $(genDir)/updated-baseline.txt " + "$(location :frameworks-base-api-current.txt) " + "$(location :frameworks-base-api-system-current.txt)", } +// Create a single file containing the latest released version of the whole +// Android module-lib API. +java_genrule { + name: "android.api.merged.module-lib.latest", + srcs: [ + ":android.api.combined.module-lib.latest", + ], + out: ["module-lib-latest.txt"], + tools: ["metalava"], + cmd: metalava_cmd + " merge-signatures --format=2.0 $(in) --out $(out)", +} + +// Make sure that the Android module-lib API is compatible with the +// previously released module-lib API. java_genrule { name: "frameworks-base-api-module-lib-current-compat", srcs: [ - ":android.api.public.latest", - ":android.api.system.latest", - ":android.api.module-lib.latest", + ":android.api.merged.module-lib.latest", ":android-incompatibilities.api.module-lib.latest", ":frameworks-base-api-current.txt", ":frameworks-base-api-system-current.txt", @@ -177,9 +215,7 @@ java_genrule { out: ["updated-baseline.txt"], tools: ["metalava"], cmd: metalava_cmd + - "--check-compatibility:api:released $(location :android.api.public.latest) " + - "--check-compatibility:api:released $(location :android.api.system.latest) " + - "--check-compatibility:api:released $(location :android.api.module-lib.latest) " + + "--check-compatibility:api:released $(location :android.api.merged.module-lib.latest) " + "--baseline:compatibility:released $(location :android-incompatibilities.api.module-lib.latest) " + "--update-baseline:compatibility:released $(genDir)/updated-baseline.txt " + "$(location :frameworks-base-api-current.txt) " + @@ -194,7 +230,7 @@ java_genrule { cmd: "$(location merge_zips) $(out) $(in)", srcs: [ ":api-stubs-docs-non-updatable{.exportable}", - ":all-modules-public-stubs-source", + ":all-modules-public-stubs-source-exportable", ], visibility: ["//visibility:private"], // Used by make module in //development, mind } @@ -351,6 +387,7 @@ stubs_defaults { "--error NoSettingsProvider", "--error UnhiddenSystemApi", "--error UnflaggedApi", + "--error FlaggedApiLiteral", "--force-convert-to-warning-nullability-annotations +*:-android.*:+android.icu.*:-dalvik.*", // Disable CallbackInterface, as Java 8 default interface methods avoid the extensibility // issue interfaces had previously. diff --git a/api/ApiDocs.bp b/api/ApiDocs.bp index e8fcf4b2b32d..1ebe0cdfabd7 100644 --- a/api/ApiDocs.bp +++ b/api/ApiDocs.bp @@ -129,7 +129,7 @@ droidstubs { droidstubs { name: "framework-doc-stubs", defaults: ["android-non-updatable-doc-stubs-defaults"], - srcs: [":all-modules-public-stubs-source"], + srcs: [":all-modules-public-stubs-source-exportable"], api_levels_module: "api_versions_public", aidl: { include_dirs: [ diff --git a/api/api.go b/api/api.go index f32bdc32f75d..5ca24de1b46a 100644 --- a/api/api.go +++ b/api/api.go @@ -429,8 +429,9 @@ func createMergedFrameworkSystemServerExportableStubs(ctx android.LoadHookContex func createPublicStubsSourceFilegroup(ctx android.LoadHookContext, modules proptools.Configurable[[]string]) { props := fgProps{} - props.Name = proptools.StringPtr("all-modules-public-stubs-source") - props.Device_common_srcs = createSrcs(modules, "{.public.stubs.source}") + props.Name = proptools.StringPtr("all-modules-public-stubs-source-exportable") + transformConfigurableArray(modules, "", ".stubs.source") + props.Device_common_srcs = createSrcs(modules, "{.exportable}") props.Visibility = []string{"//frameworks/base"} ctx.CreateModule(android.FileGroupFactory, &props) } diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp index 14e238768f41..b43905b19239 100644 --- a/cmds/bootanimation/BootAnimation.cpp +++ b/cmds/bootanimation/BootAnimation.cpp @@ -451,7 +451,7 @@ public: auto token = SurfaceComposerClient::getPhysicalDisplayToken( event.header.displayId); - auto firstDisplay = mBootAnimation->mDisplays.front(); + auto& firstDisplay = mBootAnimation->mDisplays.front(); if (token != firstDisplay.displayToken) { // ignore hotplug of a secondary display continue; diff --git a/cmds/idmap2/libidmap2/ResourceContainer.cpp b/cmds/idmap2/libidmap2/ResourceContainer.cpp index 3c0e118bbfe7..57ae3548123b 100644 --- a/cmds/idmap2/libidmap2/ResourceContainer.cpp +++ b/cmds/idmap2/libidmap2/ResourceContainer.cpp @@ -17,6 +17,7 @@ #include "idmap2/ResourceContainer.h" #include <memory> +#include <mutex> #include <string> #include <utility> #include <vector> @@ -296,7 +297,7 @@ struct ResState { } // namespace struct ApkResourceContainer : public TargetResourceContainer, public OverlayResourceContainer { - static Result<std::unique_ptr<ApkResourceContainer>> FromPath(const std::string& path); + static Result<std::unique_ptr<ApkResourceContainer>> FromPath(std::string path); // inherited from TargetResourceContainer Result<bool> DefinesOverlayable() const override; @@ -320,6 +321,7 @@ struct ApkResourceContainer : public TargetResourceContainer, public OverlayReso Result<const ResState*> GetState() const; ZipAssetsProvider* GetZipAssets() const; + mutable std::mutex state_lock_; mutable std::variant<std::unique_ptr<ZipAssetsProvider>, ResState> state_; std::string path_; }; @@ -330,16 +332,17 @@ ApkResourceContainer::ApkResourceContainer(std::unique_ptr<ZipAssetsProvider> zi } Result<std::unique_ptr<ApkResourceContainer>> ApkResourceContainer::FromPath( - const std::string& path) { + std::string path) { auto zip_assets = ZipAssetsProvider::Create(path, 0 /* flags */); if (zip_assets == nullptr) { return Error("failed to load zip assets"); } return std::unique_ptr<ApkResourceContainer>( - new ApkResourceContainer(std::move(zip_assets), path)); + new ApkResourceContainer(std::move(zip_assets), std::move(path))); } Result<const ResState*> ApkResourceContainer::GetState() const { + std::lock_guard lock(state_lock_); if (auto state = std::get_if<ResState>(&state_); state != nullptr) { return state; } @@ -355,6 +358,7 @@ Result<const ResState*> ApkResourceContainer::GetState() const { } ZipAssetsProvider* ApkResourceContainer::GetZipAssets() const { + std::lock_guard lock(state_lock_); if (auto zip = std::get_if<std::unique_ptr<ZipAssetsProvider>>(&state_); zip != nullptr) { return zip->get(); } @@ -427,7 +431,7 @@ Result<std::string> ApkResourceContainer::GetResourceName(ResourceId id) const { Result<std::unique_ptr<TargetResourceContainer>> TargetResourceContainer::FromPath( std::string path) { - auto result = ApkResourceContainer::FromPath(path); + auto result = ApkResourceContainer::FromPath(std::move(path)); if (!result) { return result.GetError(); } @@ -438,7 +442,7 @@ Result<std::unique_ptr<OverlayResourceContainer>> OverlayResourceContainer::From std::string path) { // Load the path as a fabricated overlay if the file magic indicates this is a fabricated overlay. if (android::IsFabricatedOverlay(path)) { - auto result = FabricatedOverlayContainer::FromPath(path); + auto result = FabricatedOverlayContainer::FromPath(std::move(path)); if (!result) { return result.GetError(); } @@ -446,7 +450,7 @@ Result<std::unique_ptr<OverlayResourceContainer>> OverlayResourceContainer::From } // Fallback to loading the container as an APK. - auto result = ApkResourceContainer::FromPath(path); + auto result = ApkResourceContainer::FromPath(std::move(path)); if (!result) { return result.GetError(); } diff --git a/core/api/current.txt b/core/api/current.txt index ead655426bf9..dc7ccd4efaa9 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -263,6 +263,7 @@ package android { field public static final String READ_SMS = "android.permission.READ_SMS"; field public static final String READ_SYNC_SETTINGS = "android.permission.READ_SYNC_SETTINGS"; field public static final String READ_SYNC_STATS = "android.permission.READ_SYNC_STATS"; + field @FlaggedApi("com.android.settingslib.flags.settings_catalyst") public static final String READ_SYSTEM_PREFERENCES = "android.permission.READ_SYSTEM_PREFERENCES"; field public static final String READ_VOICEMAIL = "com.android.voicemail.permission.READ_VOICEMAIL"; field public static final String REBOOT = "android.permission.REBOOT"; field public static final String RECEIVE_BOOT_COMPLETED = "android.permission.RECEIVE_BOOT_COMPLETED"; @@ -313,6 +314,7 @@ package android { field public static final String SYSTEM_ALERT_WINDOW = "android.permission.SYSTEM_ALERT_WINDOW"; field public static final String TRANSMIT_IR = "android.permission.TRANSMIT_IR"; field public static final String TURN_SCREEN_ON = "android.permission.TURN_SCREEN_ON"; + field @FlaggedApi("android.app.enable_tv_implicit_enter_pip_restriction") public static final String TV_IMPLICIT_ENTER_PIP = "android.permission.TV_IMPLICIT_ENTER_PIP"; field public static final String UNINSTALL_SHORTCUT = "com.android.launcher.permission.UNINSTALL_SHORTCUT"; field public static final String UPDATE_DEVICE_STATS = "android.permission.UPDATE_DEVICE_STATS"; field public static final String UPDATE_PACKAGES_WITHOUT_USER_ACTION = "android.permission.UPDATE_PACKAGES_WITHOUT_USER_ACTION"; @@ -334,6 +336,7 @@ package android { field public static final String WRITE_SECURE_SETTINGS = "android.permission.WRITE_SECURE_SETTINGS"; field public static final String WRITE_SETTINGS = "android.permission.WRITE_SETTINGS"; field public static final String WRITE_SYNC_SETTINGS = "android.permission.WRITE_SYNC_SETTINGS"; + field @FlaggedApi("com.android.settingslib.flags.settings_catalyst") public static final String WRITE_SYSTEM_PREFERENCES = "android.permission.WRITE_SYSTEM_PREFERENCES"; field public static final String WRITE_VOICEMAIL = "com.android.voicemail.permission.WRITE_VOICEMAIL"; } @@ -475,6 +478,8 @@ package android { field public static final int alpha = 16843551; // 0x101031f field public static final int alphabeticModifiers = 16844110; // 0x101054e field public static final int alphabeticShortcut = 16843235; // 0x10101e3 + field @FlaggedApi("android.content.pm.change_launcher_badging") public static final int alternateLauncherIcons; + field @FlaggedApi("android.content.pm.change_launcher_badging") public static final int alternateLauncherLabels; field public static final int alwaysDrawnWithCache = 16842991; // 0x10100ef field public static final int alwaysRetainTaskState = 16843267; // 0x1010203 field @Deprecated public static final int amPmBackgroundColor = 16843941; // 0x10104a5 @@ -8072,6 +8077,7 @@ package android.app.admin { method @RequiresPermission(anyOf={android.Manifest.permission.SET_TIME, "android.permission.QUERY_ADMIN_POLICY"}, conditional=true) public boolean getAutoTimeEnabled(@Nullable android.content.ComponentName); method @Deprecated public boolean getAutoTimeRequired(); method @RequiresPermission(anyOf={android.Manifest.permission.SET_TIME_ZONE, "android.permission.QUERY_ADMIN_POLICY"}, conditional=true) public boolean getAutoTimeZoneEnabled(@Nullable android.content.ComponentName); + method @FlaggedApi("android.app.admin.flags.set_auto_time_zone_enabled_coexistence") @RequiresPermission(anyOf={android.Manifest.permission.SET_TIME_ZONE, "android.permission.QUERY_ADMIN_POLICY"}, conditional=true) public int getAutoTimeZonePolicy(); method @NonNull public java.util.List<android.os.UserHandle> getBindDeviceAdminTargetUsers(@NonNull android.content.ComponentName); method public boolean getBluetoothContactSharingDisabled(@NonNull android.content.ComponentName); method @RequiresPermission(value=android.Manifest.permission.MANAGE_DEVICE_POLICY_CAMERA, conditional=true) public boolean getCameraDisabled(@Nullable android.content.ComponentName); @@ -8229,6 +8235,7 @@ package android.app.admin { method @RequiresPermission(value=android.Manifest.permission.SET_TIME, conditional=true) public void setAutoTimeEnabled(@Nullable android.content.ComponentName, boolean); method @Deprecated public void setAutoTimeRequired(@NonNull android.content.ComponentName, boolean); method @RequiresPermission(value=android.Manifest.permission.SET_TIME_ZONE, conditional=true) public void setAutoTimeZoneEnabled(@Nullable android.content.ComponentName, boolean); + method @FlaggedApi("android.app.admin.flags.set_auto_time_zone_enabled_coexistence") @RequiresPermission(value=android.Manifest.permission.SET_TIME_ZONE, conditional=true) public void setAutoTimeZonePolicy(int); method public void setBackupServiceEnabled(@NonNull android.content.ComponentName, boolean); method public void setBluetoothContactSharingDisabled(@NonNull android.content.ComponentName, boolean); method @RequiresPermission(value=android.Manifest.permission.MANAGE_DEVICE_POLICY_CAMERA, conditional=true) public void setCameraDisabled(@Nullable android.content.ComponentName, boolean); @@ -8347,6 +8354,9 @@ package android.app.admin { field public static final String ACTION_SET_NEW_PASSWORD = "android.app.action.SET_NEW_PASSWORD"; field public static final String ACTION_START_ENCRYPTION = "android.app.action.START_ENCRYPTION"; field public static final String ACTION_SYSTEM_UPDATE_POLICY_CHANGED = "android.app.action.SYSTEM_UPDATE_POLICY_CHANGED"; + field @FlaggedApi("android.app.admin.flags.set_auto_time_zone_enabled_coexistence") public static final int AUTO_TIME_ZONE_DISABLED = 1; // 0x1 + field @FlaggedApi("android.app.admin.flags.set_auto_time_zone_enabled_coexistence") public static final int AUTO_TIME_ZONE_ENABLED = 2; // 0x2 + field @FlaggedApi("android.app.admin.flags.set_auto_time_zone_enabled_coexistence") public static final int AUTO_TIME_ZONE_NOT_CONTROLLED_BY_POLICY = 0; // 0x0 field @FlaggedApi("android.view.contentprotection.flags.manage_device_policy_enabled") public static final int CONTENT_PROTECTION_DISABLED = 1; // 0x1 field @FlaggedApi("android.view.contentprotection.flags.manage_device_policy_enabled") public static final int CONTENT_PROTECTION_ENABLED = 2; // 0x2 field @FlaggedApi("android.view.contentprotection.flags.manage_device_policy_enabled") public static final int CONTENT_PROTECTION_NOT_CONTROLLED_BY_POLICY = 0; // 0x0 @@ -8787,8 +8797,31 @@ package android.app.admin { package android.app.appfunctions { + @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public final class AppFunctionException extends java.lang.Exception implements android.os.Parcelable { + ctor public AppFunctionException(int, @Nullable String); + ctor public AppFunctionException(int, @Nullable String, @NonNull android.os.Bundle); + method public int describeContents(); + method public int getErrorCategory(); + method public int getErrorCode(); + method @Nullable public String getErrorMessage(); + method @NonNull public android.os.Bundle getExtras(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.app.appfunctions.AppFunctionException> CREATOR; + field public static final int ERROR_APP_UNKNOWN_ERROR = 3000; // 0xbb8 + field public static final int ERROR_CANCELLED = 2001; // 0x7d1 + field public static final int ERROR_CATEGORY_APP = 3; // 0x3 + field public static final int ERROR_CATEGORY_REQUEST_ERROR = 1; // 0x1 + field public static final int ERROR_CATEGORY_SYSTEM = 2; // 0x2 + field public static final int ERROR_CATEGORY_UNKNOWN = 0; // 0x0 + field public static final int ERROR_DENIED = 1000; // 0x3e8 + field public static final int ERROR_DISABLED = 1002; // 0x3ea + field public static final int ERROR_FUNCTION_NOT_FOUND = 1003; // 0x3eb + field public static final int ERROR_INVALID_ARGUMENT = 1001; // 0x3e9 + field public static final int ERROR_SYSTEM_ERROR = 2000; // 0x7d0 + } + @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public final class AppFunctionManager { - method @RequiresPermission(anyOf={"android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED", "android.permission.EXECUTE_APP_FUNCTIONS"}, conditional=true) public void executeAppFunction(@NonNull android.app.appfunctions.ExecuteAppFunctionRequest, @NonNull java.util.concurrent.Executor, @NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer<android.app.appfunctions.ExecuteAppFunctionResponse>); + method @RequiresPermission(anyOf={"android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED", "android.permission.EXECUTE_APP_FUNCTIONS"}, conditional=true) public void executeAppFunction(@NonNull android.app.appfunctions.ExecuteAppFunctionRequest, @NonNull java.util.concurrent.Executor, @NonNull android.os.CancellationSignal, @NonNull android.os.OutcomeReceiver<android.app.appfunctions.ExecuteAppFunctionResponse,android.app.appfunctions.AppFunctionException>); method @RequiresPermission(anyOf={"android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED", "android.permission.EXECUTE_APP_FUNCTIONS"}, conditional=true) public void isAppFunctionEnabled(@NonNull String, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,java.lang.Exception>); method public void isAppFunctionEnabled(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,java.lang.Exception>); method public void setAppFunctionEnabled(@NonNull String, int, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,java.lang.Exception>); @@ -8800,7 +8833,7 @@ package android.app.appfunctions { @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public abstract class AppFunctionService extends android.app.Service { ctor public AppFunctionService(); method @NonNull public final android.os.IBinder onBind(@Nullable android.content.Intent); - method @MainThread public abstract void onExecuteFunction(@NonNull android.app.appfunctions.ExecuteAppFunctionRequest, @NonNull String, @NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer<android.app.appfunctions.ExecuteAppFunctionResponse>); + method @MainThread public abstract void onExecuteFunction(@NonNull android.app.appfunctions.ExecuteAppFunctionRequest, @NonNull String, @NonNull android.os.CancellationSignal, @NonNull android.os.OutcomeReceiver<android.app.appfunctions.ExecuteAppFunctionResponse,android.app.appfunctions.AppFunctionException>); field @NonNull public static final String SERVICE_INTERFACE = "android.app.appfunctions.AppFunctionService"; } @@ -8822,30 +8855,14 @@ package android.app.appfunctions { } @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public final class ExecuteAppFunctionResponse implements android.os.Parcelable { + ctor public ExecuteAppFunctionResponse(@NonNull android.app.appsearch.GenericDocument); + ctor public ExecuteAppFunctionResponse(@NonNull android.app.appsearch.GenericDocument, @NonNull android.os.Bundle); method public int describeContents(); - method public int getErrorCategory(); - method @Nullable public String getErrorMessage(); method @NonNull public android.os.Bundle getExtras(); - method public int getResultCode(); method @NonNull public android.app.appsearch.GenericDocument getResultDocument(); - method public boolean isSuccess(); - method @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") @NonNull public static android.app.appfunctions.ExecuteAppFunctionResponse newFailure(int, @Nullable String, @Nullable android.os.Bundle); - method @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") @NonNull public static android.app.appfunctions.ExecuteAppFunctionResponse newSuccess(@NonNull android.app.appsearch.GenericDocument, @Nullable android.os.Bundle); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.app.appfunctions.ExecuteAppFunctionResponse> CREATOR; - field public static final int ERROR_CATEGORY_APP = 3; // 0x3 - field public static final int ERROR_CATEGORY_REQUEST_ERROR = 1; // 0x1 - field public static final int ERROR_CATEGORY_SYSTEM = 2; // 0x2 - field public static final int ERROR_CATEGORY_UNKNOWN = 0; // 0x0 - field public static final String PROPERTY_RETURN_VALUE = "returnValue"; - field public static final int RESULT_APP_UNKNOWN_ERROR = 3000; // 0xbb8 - field public static final int RESULT_CANCELLED = 2001; // 0x7d1 - field public static final int RESULT_DENIED = 1000; // 0x3e8 - field public static final int RESULT_DISABLED = 1002; // 0x3ea - field public static final int RESULT_FUNCTION_NOT_FOUND = 1003; // 0x3eb - field public static final int RESULT_INVALID_ARGUMENT = 1001; // 0x3e9 - field public static final int RESULT_OK = 0; // 0x0 - field public static final int RESULT_SYSTEM_ERROR = 2000; // 0x7d0 + field public static final String PROPERTY_RETURN_VALUE = "androidAppfunctionsReturnValue"; } } @@ -8868,6 +8885,7 @@ package android.app.assist { method public void setWebUri(android.net.Uri); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.app.assist.AssistContent> CREATOR; + field @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public static final String EXTRA_APP_FUNCTION_DATA = "android.app.assist.extra.APP_FUNCTION_DATA"; } public class AssistStructure implements android.os.Parcelable { @@ -9096,6 +9114,46 @@ package android.app.blob { } +package android.app.jank { + + @FlaggedApi("android.app.jank.detailed_app_jank_metrics_api") public final class AppJankStats { + ctor public AppJankStats(int, @NonNull String, @Nullable String, @Nullable String, long, long, @NonNull android.app.jank.FrameOverrunHistogram); + method @NonNull public android.app.jank.FrameOverrunHistogram getFrameOverrunHistogram(); + method public long getJankyFrameCount(); + method public long getTotalFrameCount(); + method public int getUid(); + method @NonNull public String getWidgetCategory(); + method @NonNull public String getWidgetId(); + method @NonNull public String getWidgetState(); + field public static final String ANIMATING = "animating"; + field public static final String ANIMATION = "animation"; + field public static final String DRAGGING = "dragging"; + field public static final String FLINGING = "flinging"; + field public static final String KEYBOARD = "keyboard"; + field public static final String MEDIA = "media"; + field public static final String NAVIGATION = "navigation"; + field public static final String NONE = "none"; + field public static final String OTHER = "other"; + field public static final String PLAYBACK = "playback"; + field public static final String PREDICTIVE_BACK = "predictive_back"; + field public static final String SCROLL = "scroll"; + field public static final String SCROLLING = "scrolling"; + field public static final String SWIPING = "swiping"; + field public static final String TAPPING = "tapping"; + field public static final String WIDGET_CATEGORY_UNSPECIFIED = "widget_category_unspecified"; + field public static final String WIDGET_STATE_UNSPECIFIED = "widget_state_unspecified"; + field public static final String ZOOMING = "zooming"; + } + + @FlaggedApi("android.app.jank.detailed_app_jank_metrics_api") public class FrameOverrunHistogram { + ctor public FrameOverrunHistogram(); + method public void addFrameOverrunMillis(int); + method @NonNull public int[] getBucketCounters(); + method @NonNull public int[] getBucketEndpointsMillis(); + } + +} + package android.app.job { public class JobInfo implements android.os.Parcelable { @@ -9249,6 +9307,7 @@ package android.app.job { method @Nullable public abstract android.app.job.JobInfo getPendingJob(int); method public int getPendingJobReason(int); method @FlaggedApi("android.app.job.get_pending_job_reasons_api") @NonNull public int[] getPendingJobReasons(int); + method @FlaggedApi("android.app.job.get_pending_job_reasons_history_api") @NonNull public java.util.List<android.app.job.PendingJobReasonsInfo> getPendingJobReasonsHistory(int); method @NonNull public java.util.Map<java.lang.String,java.util.List<android.app.job.JobInfo>> getPendingJobsInAllNamespaces(); method public abstract int schedule(@NonNull android.app.job.JobInfo); field public static final int PENDING_JOB_REASON_APP = 1; // 0x1 @@ -9327,6 +9386,15 @@ package android.app.job { method @NonNull public android.app.job.JobWorkItem.Builder setMinimumNetworkChunkBytes(long); } + @FlaggedApi("android.app.job.get_pending_job_reasons_history_api") public final class PendingJobReasonsInfo implements android.os.Parcelable { + ctor public PendingJobReasonsInfo(long, @NonNull int[]); + method public int describeContents(); + method @NonNull public int[] getPendingJobReasons(); + method public long getTimestampMillis(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.app.job.PendingJobReasonsInfo> CREATOR; + } + } package android.app.people { @@ -13016,6 +13084,7 @@ package android.content.pm { method public boolean hasParentSessionId(); method public boolean isActive(); method public boolean isApplicationEnabledSettingPersistent(); + method @FlaggedApi("android.content.pm.sdk_dependency_installer") public boolean isAutoInstallDependenciesEnabled(); method public boolean isCommitted(); method public boolean isMultiPackage(); method public boolean isPreApprovalRequested(); @@ -13051,6 +13120,7 @@ package android.content.pm { method public void setApplicationEnabledSettingPersistent(); method @Deprecated public void setAutoRevokePermissionsMode(boolean); method public void setDontKillApp(boolean); + method @FlaggedApi("android.content.pm.sdk_dependency_installer") public void setEnableAutoInstallDependencies(boolean); method public void setInstallLocation(int); method public void setInstallReason(int); method public void setInstallScenario(int); @@ -19360,6 +19430,7 @@ package android.hardware.camera2 { field @FlaggedApi("com.android.internal.camera.flags.color_temperature") @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Range<java.lang.Integer>> COLOR_CORRECTION_COLOR_TEMPERATURE_RANGE; field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AE_AVAILABLE_ANTIBANDING_MODES; field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AE_AVAILABLE_MODES; + field @FlaggedApi("com.android.internal.camera.flags.ae_priority") @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AE_AVAILABLE_PRIORITY_MODES; field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Range<java.lang.Integer>[]> CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES; field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Range<java.lang.Integer>> CONTROL_AE_COMPENSATION_RANGE; field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Rational> CONTROL_AE_COMPENSATION_STEP; @@ -19677,6 +19748,9 @@ package android.hardware.camera2 { field public static final int CONTROL_AE_PRECAPTURE_TRIGGER_CANCEL = 2; // 0x2 field public static final int CONTROL_AE_PRECAPTURE_TRIGGER_IDLE = 0; // 0x0 field public static final int CONTROL_AE_PRECAPTURE_TRIGGER_START = 1; // 0x1 + field @FlaggedApi("com.android.internal.camera.flags.ae_priority") public static final int CONTROL_AE_PRIORITY_MODE_OFF = 0; // 0x0 + field @FlaggedApi("com.android.internal.camera.flags.ae_priority") public static final int CONTROL_AE_PRIORITY_MODE_SENSOR_EXPOSURE_TIME_PRIORITY = 2; // 0x2 + field @FlaggedApi("com.android.internal.camera.flags.ae_priority") public static final int CONTROL_AE_PRIORITY_MODE_SENSOR_SENSITIVITY_PRIORITY = 1; // 0x1 field public static final int CONTROL_AE_STATE_CONVERGED = 2; // 0x2 field public static final int CONTROL_AE_STATE_FLASH_REQUIRED = 4; // 0x4 field public static final int CONTROL_AE_STATE_INACTIVE = 0; // 0x0 @@ -19770,6 +19844,8 @@ package android.hardware.camera2 { field public static final int CONTROL_VIDEO_STABILIZATION_MODE_OFF = 0; // 0x0 field public static final int CONTROL_VIDEO_STABILIZATION_MODE_ON = 1; // 0x1 field public static final int CONTROL_VIDEO_STABILIZATION_MODE_PREVIEW_STABILIZATION = 2; // 0x2 + field @FlaggedApi("com.android.internal.camera.flags.zoom_method") public static final int CONTROL_ZOOM_METHOD_AUTO = 0; // 0x0 + field @FlaggedApi("com.android.internal.camera.flags.zoom_method") public static final int CONTROL_ZOOM_METHOD_ZOOM_RATIO = 1; // 0x1 field public static final int DISTORTION_CORRECTION_MODE_FAST = 1; // 0x1 field public static final int DISTORTION_CORRECTION_MODE_HIGH_QUALITY = 2; // 0x2 field public static final int DISTORTION_CORRECTION_MODE_OFF = 0; // 0x0 @@ -19777,6 +19853,9 @@ package android.hardware.camera2 { field public static final int EDGE_MODE_HIGH_QUALITY = 2; // 0x2 field public static final int EDGE_MODE_OFF = 0; // 0x0 field public static final int EDGE_MODE_ZERO_SHUTTER_LAG = 3; // 0x3 + field @FlaggedApi("com.android.internal.camera.flags.night_mode_indicator") public static final int EXTENSION_NIGHT_MODE_INDICATOR_OFF = 1; // 0x1 + field @FlaggedApi("com.android.internal.camera.flags.night_mode_indicator") public static final int EXTENSION_NIGHT_MODE_INDICATOR_ON = 2; // 0x2 + field @FlaggedApi("com.android.internal.camera.flags.night_mode_indicator") public static final int EXTENSION_NIGHT_MODE_INDICATOR_UNKNOWN = 0; // 0x0 field public static final int FLASH_MODE_OFF = 0; // 0x0 field public static final int FLASH_MODE_SINGLE = 1; // 0x1 field public static final int FLASH_MODE_TORCH = 2; // 0x2 @@ -19956,6 +20035,7 @@ package android.hardware.camera2 { field @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Boolean> CONTROL_AE_LOCK; field @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> CONTROL_AE_MODE; field @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> CONTROL_AE_PRECAPTURE_TRIGGER; + field @FlaggedApi("com.android.internal.camera.flags.ae_priority") @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> CONTROL_AE_PRIORITY_MODE; field @NonNull public static final android.hardware.camera2.CaptureRequest.Key<android.hardware.camera2.params.MeteringRectangle[]> CONTROL_AE_REGIONS; field @NonNull public static final android.hardware.camera2.CaptureRequest.Key<android.util.Range<java.lang.Integer>> CONTROL_AE_TARGET_FPS_RANGE; field @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> CONTROL_AF_MODE; @@ -19974,6 +20054,7 @@ package android.hardware.camera2 { field @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> CONTROL_SCENE_MODE; field @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> CONTROL_SETTINGS_OVERRIDE; field @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> CONTROL_VIDEO_STABILIZATION_MODE; + field @FlaggedApi("com.android.internal.camera.flags.zoom_method") @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> CONTROL_ZOOM_METHOD; field @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Float> CONTROL_ZOOM_RATIO; field @NonNull public static final android.os.Parcelable.Creator<android.hardware.camera2.CaptureRequest> CREATOR; field @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> DISTORTION_CORRECTION_MODE; @@ -20048,6 +20129,7 @@ package android.hardware.camera2 { field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Boolean> CONTROL_AE_LOCK; field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> CONTROL_AE_MODE; field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> CONTROL_AE_PRECAPTURE_TRIGGER; + field @FlaggedApi("com.android.internal.camera.flags.ae_priority") @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> CONTROL_AE_PRIORITY_MODE; field @NonNull public static final android.hardware.camera2.CaptureResult.Key<android.hardware.camera2.params.MeteringRectangle[]> CONTROL_AE_REGIONS; field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> CONTROL_AE_STATE; field @NonNull public static final android.hardware.camera2.CaptureResult.Key<android.util.Range<java.lang.Integer>> CONTROL_AE_TARGET_FPS_RANGE; @@ -20072,10 +20154,12 @@ package android.hardware.camera2 { field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> CONTROL_SCENE_MODE; field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> CONTROL_SETTINGS_OVERRIDE; field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> CONTROL_VIDEO_STABILIZATION_MODE; + field @FlaggedApi("com.android.internal.camera.flags.zoom_method") @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> CONTROL_ZOOM_METHOD; field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Float> CONTROL_ZOOM_RATIO; field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> DISTORTION_CORRECTION_MODE; field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> EDGE_MODE; field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> EXTENSION_CURRENT_TYPE; + field @FlaggedApi("com.android.internal.camera.flags.night_mode_indicator") @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> EXTENSION_NIGHT_MODE_INDICATOR; field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> EXTENSION_STRENGTH; field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> FLASH_MODE; field @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> FLASH_STATE; @@ -20893,6 +20977,8 @@ package android.hardware.usb { method public boolean hasPermission(android.hardware.usb.UsbDevice); method public boolean hasPermission(android.hardware.usb.UsbAccessory); method public android.os.ParcelFileDescriptor openAccessory(android.hardware.usb.UsbAccessory); + method @FlaggedApi("android.hardware.usb.flags.enable_accessory_stream_api") @NonNull public java.io.InputStream openAccessoryInputStream(@NonNull android.hardware.usb.UsbAccessory); + method @FlaggedApi("android.hardware.usb.flags.enable_accessory_stream_api") @NonNull public java.io.OutputStream openAccessoryOutputStream(@NonNull android.hardware.usb.UsbAccessory); method public android.hardware.usb.UsbDeviceConnection openDevice(android.hardware.usb.UsbDevice); method public void requestPermission(android.hardware.usb.UsbDevice, android.app.PendingIntent); method public void requestPermission(android.hardware.usb.UsbAccessory, android.app.PendingIntent); @@ -22882,6 +22968,7 @@ package android.media { method public void onCryptoError(@NonNull android.media.MediaCodec, @NonNull android.media.MediaCodec.CryptoException); method public abstract void onError(@NonNull android.media.MediaCodec, @NonNull android.media.MediaCodec.CodecException); method public abstract void onInputBufferAvailable(@NonNull android.media.MediaCodec, int); + method @FlaggedApi("android.media.codec.subsession_metrics") public void onMetricsFlushed(@NonNull android.media.MediaCodec, @NonNull android.os.PersistableBundle); method public abstract void onOutputBufferAvailable(@NonNull android.media.MediaCodec, int, @NonNull android.media.MediaCodec.BufferInfo); method @FlaggedApi("com.android.media.codec.flags.large_audio_frame") public void onOutputBuffersAvailable(@NonNull android.media.MediaCodec, int, @NonNull java.util.ArrayDeque<android.media.MediaCodec.BufferInfo>); method public abstract void onOutputFormatChanged(@NonNull android.media.MediaCodec, @NonNull android.media.MediaFormat); @@ -23973,6 +24060,7 @@ package android.media { field public static final String KEY_MPEGH_COMPATIBLE_SETS = "mpegh-compatible-sets"; field public static final String KEY_MPEGH_PROFILE_LEVEL_INDICATION = "mpegh-profile-level-indication"; field public static final String KEY_MPEGH_REFERENCE_CHANNEL_LAYOUT = "mpegh-reference-channel-layout"; + field @FlaggedApi("android.media.codec.num_input_slots") public static final String KEY_NUM_SLOTS = "num-slots"; field public static final String KEY_OPERATING_RATE = "operating-rate"; field public static final String KEY_OUTPUT_REORDER_DEPTH = "output-reorder-depth"; field public static final String KEY_PCM_ENCODING = "pcm-encoding"; @@ -37736,6 +37824,7 @@ package android.provider { field public static final String ACTION_DISPLAY_SETTINGS = "android.settings.DISPLAY_SETTINGS"; field public static final String ACTION_DREAM_SETTINGS = "android.settings.DREAM_SETTINGS"; field @Deprecated public static final String ACTION_FINGERPRINT_ENROLL = "android.settings.FINGERPRINT_ENROLL"; + field @FlaggedApi("android.provider.system_regional_preferences_api_enabled") public static final String ACTION_FIRST_DAY_OF_WEEK_SETTINGS = "android.settings.FIRST_DAY_OF_WEEK_SETTINGS"; field public static final String ACTION_HARD_KEYBOARD_SETTINGS = "android.settings.HARD_KEYBOARD_SETTINGS"; field public static final String ACTION_HOME_SETTINGS = "android.settings.HOME_SETTINGS"; field public static final String ACTION_IGNORE_BACKGROUND_DATA_RESTRICTIONS_SETTINGS = "android.settings.IGNORE_BACKGROUND_DATA_RESTRICTIONS_SETTINGS"; @@ -37756,6 +37845,7 @@ package android.provider { field public static final String ACTION_MANAGE_SUPERVISOR_RESTRICTED_SETTING = "android.settings.MANAGE_SUPERVISOR_RESTRICTED_SETTING"; field public static final String ACTION_MANAGE_UNKNOWN_APP_SOURCES = "android.settings.MANAGE_UNKNOWN_APP_SOURCES"; field public static final String ACTION_MANAGE_WRITE_SETTINGS = "android.settings.action.MANAGE_WRITE_SETTINGS"; + field @FlaggedApi("android.provider.system_regional_preferences_api_enabled") public static final String ACTION_MEASUREMENT_SYSTEM_SETTINGS = "android.settings.MEASUREMENT_SYSTEM_SETTINGS"; field public static final String ACTION_MEMORY_CARD_SETTINGS = "android.settings.MEMORY_CARD_SETTINGS"; field public static final String ACTION_NETWORK_OPERATOR_SETTINGS = "android.settings.NETWORK_OPERATOR_SETTINGS"; field public static final String ACTION_NFCSHARING_SETTINGS = "android.settings.NFCSHARING_SETTINGS"; @@ -37766,12 +37856,14 @@ package android.provider { field public static final String ACTION_NOTIFICATION_LISTENER_DETAIL_SETTINGS = "android.settings.NOTIFICATION_LISTENER_DETAIL_SETTINGS"; field public static final String ACTION_NOTIFICATION_LISTENER_SETTINGS = "android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS"; field public static final String ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS = "android.settings.NOTIFICATION_POLICY_ACCESS_SETTINGS"; + field @FlaggedApi("android.provider.system_regional_preferences_api_enabled") public static final String ACTION_NUMBERING_SYSTEM_SETTINGS = "android.settings.NUMBERING_SYSTEM_SETTINGS"; field public static final String ACTION_PRINT_SETTINGS = "android.settings.ACTION_PRINT_SETTINGS"; field public static final String ACTION_PRIVACY_SETTINGS = "android.settings.PRIVACY_SETTINGS"; field public static final String ACTION_PROCESS_WIFI_EASY_CONNECT_URI = "android.settings.PROCESS_WIFI_EASY_CONNECT_URI"; field public static final String ACTION_QUICK_ACCESS_WALLET_SETTINGS = "android.settings.QUICK_ACCESS_WALLET_SETTINGS"; field public static final String ACTION_QUICK_LAUNCH_SETTINGS = "android.settings.QUICK_LAUNCH_SETTINGS"; field public static final String ACTION_REGIONAL_PREFERENCES_SETTINGS = "android.settings.REGIONAL_PREFERENCES_SETTINGS"; + field @FlaggedApi("android.provider.system_regional_preferences_api_enabled") public static final String ACTION_REGION_SETTINGS = "android.settings.REGION_SETTINGS"; field public static final String ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS = "android.settings.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"; field public static final String ACTION_REQUEST_MANAGE_MEDIA = "android.settings.REQUEST_MANAGE_MEDIA"; field @FlaggedApi("com.android.media.flags.enable_privileged_routing_for_media_routing_control") public static final String ACTION_REQUEST_MEDIA_ROUTING_CONTROL = "android.settings.REQUEST_MEDIA_ROUTING_CONTROL"; @@ -37787,6 +37879,7 @@ package android.provider { field public static final String ACTION_SOUND_SETTINGS = "android.settings.SOUND_SETTINGS"; field @Deprecated public static final String ACTION_STORAGE_VOLUME_ACCESS_SETTINGS = "android.settings.STORAGE_VOLUME_ACCESS_SETTINGS"; field public static final String ACTION_SYNC_SETTINGS = "android.settings.SYNC_SETTINGS"; + field @FlaggedApi("android.provider.system_regional_preferences_api_enabled") public static final String ACTION_TEMPERATURE_UNIT_SETTINGS = "android.settings.TEMPERATURE_UNIT_SETTINGS"; field public static final String ACTION_USAGE_ACCESS_SETTINGS = "android.settings.USAGE_ACCESS_SETTINGS"; field public static final String ACTION_USER_DICTIONARY_SETTINGS = "android.settings.USER_DICTIONARY_SETTINGS"; field public static final String ACTION_VOICE_CONTROL_AIRPLANE_MODE = "android.settings.VOICE_CONTROL_AIRPLANE_MODE"; @@ -40371,7 +40464,7 @@ package android.security.keystore { method @NonNull public android.security.keystore.KeyProtection.Builder setUserPresenceRequired(boolean); } - @FlaggedApi("android.security.keystore_grant_api") public class KeyStoreManager { + @FlaggedApi("android.security.keystore_grant_api") public final class KeyStoreManager { method @NonNull public java.util.List<java.security.cert.X509Certificate> getGrantedCertificateChainFromId(long) throws android.security.keystore.KeyPermanentlyInvalidatedException, java.security.UnrecoverableKeyException; method @NonNull public java.security.Key getGrantedKeyFromId(long) throws android.security.keystore.KeyPermanentlyInvalidatedException, java.security.UnrecoverableKeyException; method @NonNull public java.security.KeyPair getGrantedKeyPairFromId(long) throws android.security.keystore.KeyPermanentlyInvalidatedException, java.security.UnrecoverableKeyException; @@ -40614,7 +40707,7 @@ package android.service.autofill { field @NonNull public static final android.os.Parcelable.Creator<android.service.autofill.FillRequest> CREATOR; field public static final int FLAG_COMPATIBILITY_MODE_REQUEST = 2; // 0x2 field public static final int FLAG_MANUAL_REQUEST = 1; // 0x1 - field public static final int FLAG_SUPPORTS_FILL_DIALOG = 64; // 0x40 + field @Deprecated @FlaggedApi("android.service.autofill.fill_dialog_improvements") public static final int FLAG_SUPPORTS_FILL_DIALOG = 64; // 0x40 } public final class FillResponse implements android.os.Parcelable { @@ -41999,6 +42092,193 @@ package android.service.restrictions { } +package android.service.settings.preferences { + + @FlaggedApi("com.android.settingslib.flags.settings_catalyst") public final class GetValueRequest implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public String getPreferenceKey(); + method @NonNull public String getScreenKey(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.service.settings.preferences.GetValueRequest> CREATOR; + } + + public static final class GetValueRequest.Builder { + ctor public GetValueRequest.Builder(@NonNull String, @NonNull String); + method @NonNull public android.service.settings.preferences.GetValueRequest build(); + } + + @FlaggedApi("com.android.settingslib.flags.settings_catalyst") public final class GetValueResult implements android.os.Parcelable { + method public int describeContents(); + method @Nullable public android.service.settings.preferences.SettingsPreferenceMetadata getMetadata(); + method public int getResultCode(); + method @Nullable public android.service.settings.preferences.SettingsPreferenceValue getValue(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.service.settings.preferences.GetValueResult> CREATOR; + field public static final int RESULT_DISALLOW = 4; // 0x4 + field public static final int RESULT_INTERNAL_ERROR = 6; // 0x6 + field public static final int RESULT_INVALID_REQUEST = 5; // 0x5 + field public static final int RESULT_OK = 0; // 0x0 + field public static final int RESULT_REQUIRE_APP_PERMISSION = 3; // 0x3 + field public static final int RESULT_UNAVAILABLE = 2; // 0x2 + field public static final int RESULT_UNSUPPORTED = 1; // 0x1 + } + + public static final class GetValueResult.Builder { + ctor public GetValueResult.Builder(int); + method @NonNull public android.service.settings.preferences.GetValueResult build(); + method @NonNull public android.service.settings.preferences.GetValueResult.Builder setMetadata(@Nullable android.service.settings.preferences.SettingsPreferenceMetadata); + method @NonNull public android.service.settings.preferences.GetValueResult.Builder setValue(@Nullable android.service.settings.preferences.SettingsPreferenceValue); + } + + @FlaggedApi("com.android.settingslib.flags.settings_catalyst") public final class MetadataRequest implements android.os.Parcelable { + method public int describeContents(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.service.settings.preferences.MetadataRequest> CREATOR; + } + + public static final class MetadataRequest.Builder { + ctor public MetadataRequest.Builder(); + method @NonNull public android.service.settings.preferences.MetadataRequest build(); + } + + @FlaggedApi("com.android.settingslib.flags.settings_catalyst") public final class MetadataResult implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public java.util.List<android.service.settings.preferences.SettingsPreferenceMetadata> getMetadataList(); + method public int getResultCode(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.service.settings.preferences.MetadataResult> CREATOR; + field public static final int RESULT_INTERNAL_ERROR = 2; // 0x2 + field public static final int RESULT_OK = 0; // 0x0 + field public static final int RESULT_UNSUPPORTED = 1; // 0x1 + } + + public static final class MetadataResult.Builder { + ctor public MetadataResult.Builder(int); + method @NonNull public android.service.settings.preferences.MetadataResult build(); + method @NonNull public android.service.settings.preferences.MetadataResult.Builder setMetadataList(@NonNull java.util.List<android.service.settings.preferences.SettingsPreferenceMetadata>); + } + + @FlaggedApi("com.android.settingslib.flags.settings_catalyst") public final class SetValueRequest implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public String getPreferenceKey(); + method @NonNull public android.service.settings.preferences.SettingsPreferenceValue getPreferenceValue(); + method @NonNull public String getScreenKey(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.service.settings.preferences.SetValueRequest> CREATOR; + } + + public static final class SetValueRequest.Builder { + ctor public SetValueRequest.Builder(@NonNull String, @NonNull String, @NonNull android.service.settings.preferences.SettingsPreferenceValue); + method @NonNull public android.service.settings.preferences.SetValueRequest build(); + } + + @FlaggedApi("com.android.settingslib.flags.settings_catalyst") public final class SetValueResult implements android.os.Parcelable { + method public int describeContents(); + method public int getResultCode(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.service.settings.preferences.SetValueResult> CREATOR; + field public static final int RESULT_DISABLED = 2; // 0x2 + field public static final int RESULT_DISALLOW = 7; // 0x7 + field public static final int RESULT_INTERNAL_ERROR = 9; // 0x9 + field public static final int RESULT_INVALID_REQUEST = 8; // 0x8 + field public static final int RESULT_OK = 0; // 0x0 + field public static final int RESULT_REQUIRE_APP_PERMISSION = 5; // 0x5 + field public static final int RESULT_REQUIRE_USER_CONSENT = 6; // 0x6 + field public static final int RESULT_RESTRICTED = 3; // 0x3 + field public static final int RESULT_UNAVAILABLE = 4; // 0x4 + field public static final int RESULT_UNSUPPORTED = 1; // 0x1 + } + + public static final class SetValueResult.Builder { + ctor public SetValueResult.Builder(int); + method @NonNull public android.service.settings.preferences.SetValueResult build(); + } + + @FlaggedApi("com.android.settingslib.flags.settings_catalyst") public final class SettingsPreferenceMetadata implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public java.util.List<java.lang.String> getBreadcrumbs(); + method @NonNull public android.os.Bundle getExtras(); + method @NonNull public String getKey(); + method @Nullable public android.app.PendingIntent getLaunchIntent(); + method @NonNull public java.util.List<java.lang.String> getReadPermissions(); + method @NonNull public String getScreenKey(); + method @Nullable public String getSummary(); + method @Nullable public String getTitle(); + method @NonNull public java.util.List<java.lang.String> getWritePermissions(); + method public int getWriteSensitivity(); + method public boolean isAvailable(); + method public boolean isEnabled(); + method public boolean isRestricted(); + method public boolean isWritable(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.service.settings.preferences.SettingsPreferenceMetadata> CREATOR; + field public static final int INTENT_ONLY = 2; // 0x2 + field public static final int NOT_SENSITIVE = 0; // 0x0 + field public static final int SENSITIVE = 1; // 0x1 + } + + public static final class SettingsPreferenceMetadata.Builder { + ctor public SettingsPreferenceMetadata.Builder(@NonNull String, @NonNull String); + method @NonNull public android.service.settings.preferences.SettingsPreferenceMetadata build(); + method @NonNull public android.service.settings.preferences.SettingsPreferenceMetadata.Builder setAvailable(boolean); + method @NonNull public android.service.settings.preferences.SettingsPreferenceMetadata.Builder setBreadcrumbs(@NonNull java.util.List<java.lang.String>); + method @NonNull public android.service.settings.preferences.SettingsPreferenceMetadata.Builder setEnabled(boolean); + method @NonNull public android.service.settings.preferences.SettingsPreferenceMetadata.Builder setExtras(@NonNull android.os.Bundle); + method @NonNull public android.service.settings.preferences.SettingsPreferenceMetadata.Builder setLaunchIntent(@Nullable android.app.PendingIntent); + method @NonNull public android.service.settings.preferences.SettingsPreferenceMetadata.Builder setReadPermissions(@NonNull java.util.List<java.lang.String>); + method @NonNull public android.service.settings.preferences.SettingsPreferenceMetadata.Builder setRestricted(boolean); + method @NonNull public android.service.settings.preferences.SettingsPreferenceMetadata.Builder setSummary(@Nullable String); + method @NonNull public android.service.settings.preferences.SettingsPreferenceMetadata.Builder setTitle(@Nullable String); + method @NonNull public android.service.settings.preferences.SettingsPreferenceMetadata.Builder setWritable(boolean); + method @NonNull public android.service.settings.preferences.SettingsPreferenceMetadata.Builder setWritePermissions(@NonNull java.util.List<java.lang.String>); + method @NonNull public android.service.settings.preferences.SettingsPreferenceMetadata.Builder setWriteSensitivity(int); + } + + @FlaggedApi("com.android.settingslib.flags.settings_catalyst") public abstract class SettingsPreferenceService extends android.app.Service { + ctor public SettingsPreferenceService(); + method @NonNull public final android.os.IBinder onBind(@Nullable android.content.Intent); + method public abstract void onGetAllPreferenceMetadata(@NonNull android.service.settings.preferences.MetadataRequest, @NonNull android.os.OutcomeReceiver<android.service.settings.preferences.MetadataResult,java.lang.Exception>); + method public abstract void onGetPreferenceValue(@NonNull android.service.settings.preferences.GetValueRequest, @NonNull android.os.OutcomeReceiver<android.service.settings.preferences.GetValueResult,java.lang.Exception>); + method public abstract void onSetPreferenceValue(@NonNull android.service.settings.preferences.SetValueRequest, @NonNull android.os.OutcomeReceiver<android.service.settings.preferences.SetValueResult,java.lang.Exception>); + field public static final String ACTION_PREFERENCE_SERVICE = "android.service.settings.preferences.action.PREFERENCE_SERVICE"; + } + + @FlaggedApi("com.android.settingslib.flags.settings_catalyst") public class SettingsPreferenceServiceClient implements java.lang.AutoCloseable { + ctor public SettingsPreferenceServiceClient(@NonNull android.content.Context, @NonNull String); + method public void close(); + method public void getAllPreferenceMetadata(@NonNull android.service.settings.preferences.MetadataRequest, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.service.settings.preferences.MetadataResult,java.lang.Exception>); + method public void getPreferenceValue(@NonNull android.service.settings.preferences.GetValueRequest, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.service.settings.preferences.GetValueResult,java.lang.Exception>); + method public void setPreferenceValue(@NonNull android.service.settings.preferences.SetValueRequest, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.service.settings.preferences.SetValueResult,java.lang.Exception>); + method public void start(); + method public void stop(); + } + + @FlaggedApi("com.android.settingslib.flags.settings_catalyst") public final class SettingsPreferenceValue implements android.os.Parcelable { + method public int describeContents(); + method public boolean getBooleanValue(); + method public double getDoubleValue(); + method public long getLongValue(); + method @Nullable public String getStringValue(); + method public int getType(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.service.settings.preferences.SettingsPreferenceValue> CREATOR; + field public static final int TYPE_BOOLEAN = 0; // 0x0 + field public static final int TYPE_DOUBLE = 2; // 0x2 + field public static final int TYPE_LONG = 1; // 0x1 + field public static final int TYPE_STRING = 3; // 0x3 + } + + public static final class SettingsPreferenceValue.Builder { + ctor public SettingsPreferenceValue.Builder(int); + method @NonNull public android.service.settings.preferences.SettingsPreferenceValue build(); + method @NonNull public android.service.settings.preferences.SettingsPreferenceValue.Builder setBooleanValue(boolean); + method @NonNull public android.service.settings.preferences.SettingsPreferenceValue.Builder setDoubleValue(double); + method @NonNull public android.service.settings.preferences.SettingsPreferenceValue.Builder setLongValue(long); + method @NonNull public android.service.settings.preferences.SettingsPreferenceValue.Builder setStringValue(@Nullable String); + } + +} + package android.service.textservice { public abstract class SpellCheckerService extends android.app.Service { @@ -44104,6 +44384,8 @@ package android.telephony { field public static final String ACTION_CARRIER_CONFIG_CHANGED = "android.telephony.action.CARRIER_CONFIG_CHANGED"; field public static final int CARRIER_NR_AVAILABILITY_NSA = 1; // 0x1 field public static final int CARRIER_NR_AVAILABILITY_SA = 2; // 0x2 + field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final int CARRIER_ROAMING_NTN_CONNECT_AUTOMATIC = 0; // 0x0 + field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final int CARRIER_ROAMING_NTN_CONNECT_MANUAL = 1; // 0x1 field public static final int CROSS_SIM_SPN_FORMAT_CARRIER_NAME_ONLY = 0; // 0x0 field public static final int CROSS_SIM_SPN_FORMAT_CARRIER_NAME_WITH_BRANDING = 1; // 0x1 field public static final int DATA_CYCLE_THRESHOLD_DISABLED = -2; // 0xfffffffe @@ -44174,11 +44456,14 @@ package android.telephony { field public static final String KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY = "carrier_nr_availabilities_int_array"; field public static final String KEY_CARRIER_PROVISIONS_WIFI_MERGED_NETWORKS_BOOL = "carrier_provisions_wifi_merged_networks_bool"; field public static final String KEY_CARRIER_RCS_PROVISIONING_REQUIRED_BOOL = "carrier_rcs_provisioning_required_bool"; + field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final String KEY_CARRIER_ROAMING_NTN_CONNECT_TYPE_INT = "carrier_roaming_ntn_connect_type_int"; field @FlaggedApi("com.android.internal.telephony.flags.carrier_roaming_nb_iot_ntn") public static final String KEY_CARRIER_ROAMING_NTN_EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_INT = "carrier_roaming_ntn_emergency_call_to_satellite_handover_type_int"; + field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final String KEY_CARRIER_ROAMING_SATELLITE_DEFAULT_SERVICES_INT_ARRAY = "carrier_roaming_satellite_default_services_int_array"; field public static final String KEY_CARRIER_SERVICE_NAME_STRING_ARRAY = "carrier_service_name_array"; field public static final String KEY_CARRIER_SERVICE_NUMBER_STRING_ARRAY = "carrier_service_number_array"; field public static final String KEY_CARRIER_SETTINGS_ACTIVITY_COMPONENT_NAME_STRING = "carrier_settings_activity_component_name_string"; field public static final String KEY_CARRIER_SETTINGS_ENABLE_BOOL = "carrier_settings_enable_bool"; + field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final String KEY_CARRIER_SUPPORTED_SATELLITE_NOTIFICATION_HYSTERESIS_SEC_INT = "carrier_supported_satellite_notification_hysteresis_sec_int"; field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final String KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE = "carrier_supported_satellite_services_per_provider_bundle"; field public static final String KEY_CARRIER_SUPPORTS_OPP_DATA_AUTO_PROVISIONING_BOOL = "carrier_supports_opp_data_auto_provisioning_bool"; field public static final String KEY_CARRIER_SUPPORTS_SS_OVER_UT_BOOL = "carrier_supports_ss_over_ut_bool"; @@ -44229,6 +44514,7 @@ package android.telephony { field public static final String KEY_DIAL_STRING_REPLACE_STRING_ARRAY = "dial_string_replace_string_array"; field public static final String KEY_DISABLE_CDMA_ACTIVATION_CODE_BOOL = "disable_cdma_activation_code_bool"; field public static final String KEY_DISABLE_CHARGE_INDICATION_BOOL = "disable_charge_indication_bool"; + field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final String KEY_DISABLE_DUN_APN_WHILE_ROAMING_WITH_PRESET_APN_BOOL = "disable_dun_apn_while_roaming_with_preset_apn_bool"; field public static final String KEY_DISABLE_SUPPLEMENTARY_SERVICES_IN_AIRPLANE_MODE_BOOL = "disable_supplementary_services_in_airplane_mode_bool"; field public static final String KEY_DISCONNECT_CAUSE_PLAY_BUSYTONE_INT_ARRAY = "disconnect_cause_play_busytone_int_array"; field public static final String KEY_DISPLAY_CALL_STRENGTH_INDICATOR_BOOL = "display_call_strength_indicator_bool"; @@ -44241,6 +44527,8 @@ package android.telephony { field public static final String KEY_EDITABLE_VOICEMAIL_NUMBER_SETTING_BOOL = "editable_voicemail_number_setting_bool"; field public static final String KEY_EDITABLE_WFC_MODE_BOOL = "editable_wfc_mode_bool"; field public static final String KEY_EDITABLE_WFC_ROAMING_MODE_BOOL = "editable_wfc_roaming_mode_bool"; + field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final String KEY_EMERGENCY_CALL_TO_SATELLITE_T911_HANDOVER_TIMEOUT_MILLIS_INT = "emergency_call_to_satellite_t911_handover_timeout_millis_int"; + field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final String KEY_EMERGENCY_MESSAGING_SUPPORTED_BOOL = "emergency_messaging_supported_bool"; field public static final String KEY_EMERGENCY_NOTIFICATION_DELAY_INT = "emergency_notification_delay_int"; field public static final String KEY_EMERGENCY_NUMBER_PREFIX_STRING_ARRAY = "emergency_number_prefix_string_array"; field public static final String KEY_ENABLE_CROSS_SIM_CALLING_ON_OPPORTUNISTIC_DATA_BOOL = "enable_cross_sim_calling_on_opportunistic_data_bool"; @@ -44287,6 +44575,7 @@ package android.telephony { field public static final String KEY_MMS_MAX_IMAGE_HEIGHT_INT = "maxImageHeight"; field public static final String KEY_MMS_MAX_IMAGE_WIDTH_INT = "maxImageWidth"; field public static final String KEY_MMS_MAX_MESSAGE_SIZE_INT = "maxMessageSize"; + field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final String KEY_MMS_MAX_NTN_PAYLOAD_SIZE_BYTES_INT = "mms_max_ntn_payload_size_bytes_int"; field public static final String KEY_MMS_MESSAGE_TEXT_MAX_SIZE_INT = "maxMessageTextSize"; field public static final String KEY_MMS_MMS_DELIVERY_REPORT_ENABLED_BOOL = "enableMMSDeliveryReports"; field public static final String KEY_MMS_MMS_ENABLED_BOOL = "enabledMMS"; @@ -44325,6 +44614,7 @@ package android.telephony { field public static final String KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSSNR_INT = "opportunistic_network_exit_threshold_rssnr_int"; field public static final String KEY_OPPORTUNISTIC_NETWORK_MAX_BACKOFF_TIME_LONG = "opportunistic_network_max_backoff_time_long"; field public static final String KEY_OPPORTUNISTIC_NETWORK_PING_PONG_TIME_LONG = "opportunistic_network_ping_pong_time_long"; + field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final String KEY_OVERRIDE_WFC_ROAMING_MODE_WHILE_USING_NTN_BOOL = "override_wfc_roaming_mode_while_using_ntn_bool"; field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final String KEY_PARAMETERS_USED_FOR_NTN_LTE_SIGNAL_BAR_INT = "parameters_used_for_ntn_lte_signal_bar_int"; field public static final String KEY_PING_TEST_BEFORE_DATA_SWITCH_BOOL = "ping_test_before_data_switch_bool"; field public static final String KEY_PREFER_2G_BOOL = "prefer_2g_bool"; @@ -44343,6 +44633,7 @@ package android.telephony { field public static final String KEY_RCS_CONFIG_SERVER_URL_STRING = "rcs_config_server_url_string"; field public static final String KEY_READ_ONLY_APN_FIELDS_STRING_ARRAY = "read_only_apn_fields_string_array"; field public static final String KEY_READ_ONLY_APN_TYPES_STRING_ARRAY = "read_only_apn_types_string_array"; + field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final String KEY_REMOVE_SATELLITE_PLMN_IN_MANUAL_NETWORK_SCAN_BOOL = "remove_satellite_plmn_in_manual_network_scan_bool"; field public static final String KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL = "require_entitlement_checks_bool"; field @Deprecated public static final String KEY_RESTART_RADIO_ON_PDP_FAIL_REGULAR_DEACTIVATION_BOOL = "restart_radio_on_pdp_fail_regular_deactivation_bool"; field public static final String KEY_RTT_AUTO_UPGRADE_BOOL = "rtt_auto_upgrade_bool"; @@ -44354,13 +44645,18 @@ package android.telephony { field public static final String KEY_RTT_UPGRADE_SUPPORTED_FOR_DOWNGRADED_VT_CALL_BOOL = "rtt_upgrade_supported_for_downgraded_vt_call"; field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final String KEY_SATELLITE_ATTACH_SUPPORTED_BOOL = "satellite_attach_supported_bool"; field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final String KEY_SATELLITE_CONNECTION_HYSTERESIS_SEC_INT = "satellite_connection_hysteresis_sec_int"; + field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final String KEY_SATELLITE_DATA_SUPPORT_MODE_INT = "satellite_data_support_mode_int"; + field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final String KEY_SATELLITE_ENTITLEMENT_APP_NAME_STRING = "satellite_entitlement_app_name_string"; field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final String KEY_SATELLITE_ENTITLEMENT_STATUS_REFRESH_DAYS_INT = "satellite_entitlement_status_refresh_days_int"; field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final String KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL = "satellite_entitlement_supported_bool"; field @FlaggedApi("com.android.internal.telephony.flags.carrier_roaming_nb_iot_ntn") public static final String KEY_SATELLITE_ESOS_SUPPORTED_BOOL = "satellite_esos_supported_bool"; + field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final String KEY_SATELLITE_INFORMATION_REDIRECT_URL_STRING = "satellite_information_redirect_url_string"; + field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final String KEY_SATELLITE_NIDD_APN_NAME_STRING = "satellite_nidd_apn_name_string"; field @FlaggedApi("com.android.internal.telephony.flags.carrier_roaming_nb_iot_ntn") public static final String KEY_SATELLITE_ROAMING_ESOS_INACTIVITY_TIMEOUT_SEC_INT = "satellite_roaming_esos_inactivity_timeout_sec_int"; field @FlaggedApi("com.android.internal.telephony.flags.carrier_roaming_nb_iot_ntn") public static final String KEY_SATELLITE_ROAMING_P2P_SMS_INACTIVITY_TIMEOUT_SEC_INT = "satellite_roaming_p2p_sms_inactivity_timeout_sec_int"; field @FlaggedApi("com.android.internal.telephony.flags.carrier_roaming_nb_iot_ntn") public static final String KEY_SATELLITE_ROAMING_P2P_SMS_SUPPORTED_BOOL = "satellite_roaming_p2p_sms_supported_bool"; field @FlaggedApi("com.android.internal.telephony.flags.carrier_roaming_nb_iot_ntn") public static final String KEY_SATELLITE_ROAMING_SCREEN_OFF_INACTIVITY_TIMEOUT_SEC_INT = "satellite_roaming_screen_off_inactivity_timeout_sec_int"; + field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final String KEY_SATELLITE_ROAMING_TURN_OFF_SESSION_FOR_EMERGENCY_CALL_BOOL = "satellite_roaming_turn_off_session_for_emergency_call_bool"; field public static final String KEY_SHOW_4G_FOR_3G_DATA_ICON_BOOL = "show_4g_for_3g_data_icon_bool"; field public static final String KEY_SHOW_4G_FOR_LTE_DATA_ICON_BOOL = "show_4g_for_lte_data_icon_bool"; field public static final String KEY_SHOW_APN_SETTING_CDMA_BOOL = "show_apn_setting_cdma_bool"; @@ -44430,6 +44726,9 @@ package android.telephony { field public static final String KEY_WORLD_MODE_ENABLED_BOOL = "world_mode_enabled_bool"; field public static final String KEY_WORLD_PHONE_BOOL = "world_phone_bool"; field public static final String REMOVE_GROUP_UUID_STRING = "00000000-0000-0000-0000-000000000000"; + field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final int SATELLITE_DATA_SUPPORT_ALL = 2; // 0x2 + field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final int SATELLITE_DATA_SUPPORT_BANDWIDTH_CONSTRAINED = 1; // 0x1 + field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final int SATELLITE_DATA_SUPPORT_ONLY_RESTRICTED = 0; // 0x0 field public static final int SERVICE_CLASS_NONE = 0; // 0x0 field public static final int SERVICE_CLASS_VOICE = 1; // 0x1 field public static final int USSD_OVER_CS_ONLY = 2; // 0x2 @@ -51799,6 +52098,7 @@ package android.view { field public static final int KEYCODE_CHANNEL_DOWN = 167; // 0xa7 field public static final int KEYCODE_CHANNEL_UP = 166; // 0xa6 field public static final int KEYCODE_CLEAR = 28; // 0x1c + field @FlaggedApi("com.android.hardware.input.enable_new_25q2_keycodes") public static final int KEYCODE_CLOSE = 321; // 0x141 field public static final int KEYCODE_COMMA = 55; // 0x37 field public static final int KEYCODE_CONTACTS = 207; // 0xcf field public static final int KEYCODE_COPY = 278; // 0x116 @@ -51811,6 +52111,8 @@ package android.view { field public static final int KEYCODE_DEMO_APP_2 = 302; // 0x12e field public static final int KEYCODE_DEMO_APP_3 = 303; // 0x12f field public static final int KEYCODE_DEMO_APP_4 = 304; // 0x130 + field @FlaggedApi("com.android.hardware.input.enable_new_25q2_keycodes") public static final int KEYCODE_DICTATE = 319; // 0x13f + field @FlaggedApi("com.android.hardware.input.enable_new_25q2_keycodes") public static final int KEYCODE_DO_NOT_DISTURB = 322; // 0x142 field public static final int KEYCODE_DPAD_CENTER = 23; // 0x17 field public static final int KEYCODE_DPAD_DOWN = 20; // 0x14 field public static final int KEYCODE_DPAD_DOWN_LEFT = 269; // 0x10d @@ -51823,7 +52125,7 @@ package android.view { field public static final int KEYCODE_DVR = 173; // 0xad field public static final int KEYCODE_E = 33; // 0x21 field public static final int KEYCODE_EISU = 212; // 0xd4 - field @FlaggedApi("com.android.hardware.input.emoji_and_screenshot_keycodes_available") public static final int KEYCODE_EMOJI_PICKER = 317; // 0x13d + field public static final int KEYCODE_EMOJI_PICKER = 317; // 0x13d field public static final int KEYCODE_ENDCALL = 6; // 0x6 field public static final int KEYCODE_ENTER = 66; // 0x42 field public static final int KEYCODE_ENVELOPE = 65; // 0x41 @@ -51835,7 +52137,19 @@ package android.view { field public static final int KEYCODE_F10 = 140; // 0x8c field public static final int KEYCODE_F11 = 141; // 0x8d field public static final int KEYCODE_F12 = 142; // 0x8e + field @FlaggedApi("com.android.hardware.input.enable_new_25q2_keycodes") public static final int KEYCODE_F13 = 326; // 0x146 + field @FlaggedApi("com.android.hardware.input.enable_new_25q2_keycodes") public static final int KEYCODE_F14 = 327; // 0x147 + field @FlaggedApi("com.android.hardware.input.enable_new_25q2_keycodes") public static final int KEYCODE_F15 = 328; // 0x148 + field @FlaggedApi("com.android.hardware.input.enable_new_25q2_keycodes") public static final int KEYCODE_F16 = 329; // 0x149 + field @FlaggedApi("com.android.hardware.input.enable_new_25q2_keycodes") public static final int KEYCODE_F17 = 330; // 0x14a + field @FlaggedApi("com.android.hardware.input.enable_new_25q2_keycodes") public static final int KEYCODE_F18 = 331; // 0x14b + field @FlaggedApi("com.android.hardware.input.enable_new_25q2_keycodes") public static final int KEYCODE_F19 = 332; // 0x14c field public static final int KEYCODE_F2 = 132; // 0x84 + field @FlaggedApi("com.android.hardware.input.enable_new_25q2_keycodes") public static final int KEYCODE_F20 = 333; // 0x14d + field @FlaggedApi("com.android.hardware.input.enable_new_25q2_keycodes") public static final int KEYCODE_F21 = 334; // 0x14e + field @FlaggedApi("com.android.hardware.input.enable_new_25q2_keycodes") public static final int KEYCODE_F22 = 335; // 0x14f + field @FlaggedApi("com.android.hardware.input.enable_new_25q2_keycodes") public static final int KEYCODE_F23 = 336; // 0x150 + field @FlaggedApi("com.android.hardware.input.enable_new_25q2_keycodes") public static final int KEYCODE_F24 = 337; // 0x151 field public static final int KEYCODE_F3 = 133; // 0x85 field public static final int KEYCODE_F4 = 134; // 0x86 field public static final int KEYCODE_F5 = 135; // 0x87 @@ -51850,6 +52164,7 @@ package android.view { field public static final int KEYCODE_FOCUS = 80; // 0x50 field public static final int KEYCODE_FORWARD = 125; // 0x7d field public static final int KEYCODE_FORWARD_DEL = 112; // 0x70 + field @FlaggedApi("com.android.hardware.input.enable_new_25q2_keycodes") public static final int KEYCODE_FULLSCREEN = 325; // 0x145 field public static final int KEYCODE_FUNCTION = 119; // 0x77 field public static final int KEYCODE_G = 35; // 0x23 field public static final int KEYCODE_GRAVE = 68; // 0x44 @@ -51873,6 +52188,7 @@ package android.view { field public static final int KEYCODE_LANGUAGE_SWITCH = 204; // 0xcc field public static final int KEYCODE_LAST_CHANNEL = 229; // 0xe5 field public static final int KEYCODE_LEFT_BRACKET = 71; // 0x47 + field @FlaggedApi("com.android.hardware.input.enable_new_25q2_keycodes") public static final int KEYCODE_LOCK = 324; // 0x144 field public static final int KEYCODE_M = 41; // 0x29 field public static final int KEYCODE_MACRO_1 = 313; // 0x139 field public static final int KEYCODE_MACRO_2 = 314; // 0x13a @@ -51910,6 +52226,7 @@ package android.view { field public static final int KEYCODE_NAVIGATE_NEXT = 261; // 0x105 field public static final int KEYCODE_NAVIGATE_OUT = 263; // 0x107 field public static final int KEYCODE_NAVIGATE_PREVIOUS = 260; // 0x104 + field @FlaggedApi("com.android.hardware.input.enable_new_25q2_keycodes") public static final int KEYCODE_NEW = 320; // 0x140 field public static final int KEYCODE_NOTIFICATION = 83; // 0x53 field public static final int KEYCODE_NUM = 78; // 0x4e field public static final int KEYCODE_NUMPAD_0 = 144; // 0x90 @@ -51944,6 +52261,7 @@ package android.view { field public static final int KEYCODE_PLUS = 81; // 0x51 field public static final int KEYCODE_POUND = 18; // 0x12 field public static final int KEYCODE_POWER = 26; // 0x1a + field @FlaggedApi("com.android.hardware.input.enable_new_25q2_keycodes") public static final int KEYCODE_PRINT = 323; // 0x143 field public static final int KEYCODE_PROFILE_SWITCH = 288; // 0x120 field public static final int KEYCODE_PROG_BLUE = 186; // 0xba field public static final int KEYCODE_PROG_GREEN = 184; // 0xb8 @@ -51956,7 +52274,7 @@ package android.view { field public static final int KEYCODE_RIGHT_BRACKET = 72; // 0x48 field public static final int KEYCODE_RO = 217; // 0xd9 field public static final int KEYCODE_S = 47; // 0x2f - field @FlaggedApi("com.android.hardware.input.emoji_and_screenshot_keycodes_available") public static final int KEYCODE_SCREENSHOT = 318; // 0x13e + field public static final int KEYCODE_SCREENSHOT = 318; // 0x13e field public static final int KEYCODE_SCROLL_LOCK = 116; // 0x74 field public static final int KEYCODE_SEARCH = 84; // 0x54 field public static final int KEYCODE_SEMICOLON = 74; // 0x4a @@ -53481,6 +53799,7 @@ package android.view { method public void removeOnAttachStateChangeListener(android.view.View.OnAttachStateChangeListener); method public void removeOnLayoutChangeListener(android.view.View.OnLayoutChangeListener); method public void removeOnUnhandledKeyEventListener(android.view.View.OnUnhandledKeyEventListener); + method @FlaggedApi("android.app.jank.detailed_app_jank_metrics_api") public void reportAppJankStats(@NonNull android.app.jank.AppJankStats); method public void requestApplyInsets(); method @Deprecated public void requestFitSystemWindows(); method public final boolean requestFocus(); @@ -53734,6 +54053,7 @@ package android.view { field @FlaggedApi("com.android.window.flags.delegate_unhandled_drags") public static final int DRAG_FLAG_GLOBAL_SAME_APPLICATION = 4096; // 0x1000 field public static final int DRAG_FLAG_GLOBAL_URI_READ = 1; // 0x1 field public static final int DRAG_FLAG_GLOBAL_URI_WRITE = 2; // 0x2 + field @FlaggedApi("com.android.window.flags.supports_drag_assistant_to_multiwindow") public static final int DRAG_FLAG_HIDE_CALLING_TASK_ON_DRAG_START = 16384; // 0x4000 field public static final int DRAG_FLAG_OPAQUE = 512; // 0x200 field @FlaggedApi("com.android.window.flags.delegate_unhandled_drags") public static final int DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG = 8192; // 0x2000 field @Deprecated public static final int DRAWING_CACHE_QUALITY_AUTO = 0; // 0x0 @@ -55410,7 +55730,7 @@ package android.view.accessibility { method public android.os.Bundle getExtras(); method public CharSequence getHintText(); method public int getInputType(); - method public android.view.accessibility.AccessibilityNodeInfo getLabelFor(); + method @Deprecated @FlaggedApi("android.view.accessibility.deprecate_ani_label_for_apis") public android.view.accessibility.AccessibilityNodeInfo getLabelFor(); method @Deprecated @FlaggedApi("android.view.accessibility.support_multiple_labeledby") public android.view.accessibility.AccessibilityNodeInfo getLabeledBy(); method @FlaggedApi("android.view.accessibility.support_multiple_labeledby") @NonNull public java.util.List<android.view.accessibility.AccessibilityNodeInfo> getLabeledByList(); method public int getLiveRegion(); @@ -55509,8 +55829,8 @@ package android.view.accessibility { method public void setHintText(CharSequence); method public void setImportantForAccessibility(boolean); method public void setInputType(int); - method public void setLabelFor(android.view.View); - method public void setLabelFor(android.view.View, int); + method @Deprecated @FlaggedApi("android.view.accessibility.deprecate_ani_label_for_apis") public void setLabelFor(android.view.View); + method @Deprecated @FlaggedApi("android.view.accessibility.deprecate_ani_label_for_apis") public void setLabelFor(android.view.View, int); method @Deprecated @FlaggedApi("android.view.accessibility.support_multiple_labeledby") public void setLabeledBy(android.view.View); method @Deprecated @FlaggedApi("android.view.accessibility.support_multiple_labeledby") public void setLabeledBy(android.view.View, int); method public void setLiveRegion(int); @@ -56228,13 +56548,13 @@ package android.view.autofill { method public void notifyViewExited(@NonNull android.view.View, int); method public void notifyViewVisibilityChanged(@NonNull android.view.View, boolean); method public void notifyViewVisibilityChanged(@NonNull android.view.View, int, boolean); - method public void notifyVirtualViewsReady(@NonNull android.view.View, @NonNull android.util.SparseArray<android.view.autofill.VirtualViewFillInfo>); + method @Deprecated @FlaggedApi("android.service.autofill.fill_dialog_improvements") public void notifyVirtualViewsReady(@NonNull android.view.View, @NonNull android.util.SparseArray<android.view.autofill.VirtualViewFillInfo>); method public void registerCallback(@Nullable android.view.autofill.AutofillManager.AutofillCallback); method public void requestAutofill(@NonNull android.view.View); method public void requestAutofill(@NonNull android.view.View, int, @NonNull android.graphics.Rect); method public void setUserData(@Nullable android.service.autofill.UserData); - method public boolean showAutofillDialog(@NonNull android.view.View); - method public boolean showAutofillDialog(@NonNull android.view.View, int); + method @Deprecated @FlaggedApi("android.service.autofill.fill_dialog_improvements") public boolean showAutofillDialog(@NonNull android.view.View); + method @Deprecated @FlaggedApi("android.service.autofill.fill_dialog_improvements") public boolean showAutofillDialog(@NonNull android.view.View, int); method public void unregisterCallback(@Nullable android.view.autofill.AutofillManager.AutofillCallback); field public static final String EXTRA_ASSIST_STRUCTURE = "android.view.autofill.extra.ASSIST_STRUCTURE"; field public static final String EXTRA_AUTHENTICATION_RESULT = "android.view.autofill.extra.AUTHENTICATION_RESULT"; diff --git a/core/api/lint-baseline.txt b/core/api/lint-baseline.txt index 1dcafd1d80ea..4ada53e1bf34 100644 --- a/core/api/lint-baseline.txt +++ b/core/api/lint-baseline.txt @@ -383,6 +383,36 @@ DeprecationMismatch: javax.microedition.khronos.egl.EGL10#eglCreatePixmapSurface Method javax.microedition.khronos.egl.EGL10.eglCreatePixmapSurface(javax.microedition.khronos.egl.EGLDisplay, javax.microedition.khronos.egl.EGLConfig, Object, int[]): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match +FlaggedApiLiteral: android.Manifest.permission#BIND_APP_FUNCTION_SERVICE: + @FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER). +FlaggedApiLiteral: android.Manifest.permission#BIND_TV_AD_SERVICE: + @FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.media.tv.flags.Flags.FLAG_ENABLE_AD_SERVICE_FW). +FlaggedApiLiteral: android.Manifest.permission#MANAGE_DEVICE_POLICY_THREAD_NETWORK: + @FlaggedApi contains a string literal, but should reference the field generated by aconfig (com.android.net.thread.platform.flags.Flags.FLAG_THREAD_USER_RESTRICTION_ENABLED). +FlaggedApiLiteral: android.Manifest.permission#QUERY_ADVANCED_PROTECTION_MODE: + @FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.security.Flags.FLAG_AAPM_API). +FlaggedApiLiteral: android.Manifest.permission#RANGING: + @FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.permission.flags.Flags.FLAG_RANGING_PERMISSION_ENABLED). +FlaggedApiLiteral: android.Manifest.permission#REQUEST_OBSERVE_DEVICE_UUID_PRESENCE: + @FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.companion.Flags.FLAG_DEVICE_PRESENCE). +FlaggedApiLiteral: android.R.attr#adServiceTypes: + @FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.media.tv.flags.Flags.FLAG_ENABLE_AD_SERVICE_FW). +FlaggedApiLiteral: android.R.attr#intentMatchingFlags: + @FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.security.Flags.FLAG_ENABLE_INTENT_MATCHING_FLAGS). +FlaggedApiLiteral: android.R.attr#languageSettingsActivity: + @FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.view.inputmethod.Flags.FLAG_IME_SWITCHER_REVAMP_API). +FlaggedApiLiteral: android.R.attr#optional: + @FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.content.pm.Flags.FLAG_SDK_LIB_INDEPENDENCE). +FlaggedApiLiteral: android.R.attr#supplementalDescription: + @FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.view.accessibility.Flags.FLAG_SUPPLEMENTAL_DESCRIPTION). +FlaggedApiLiteral: android.accessibilityservice.AccessibilityService#OVERLAY_RESULT_INTERNAL_ERROR: + @FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.view.accessibility.Flags.FLAG_A11Y_OVERLAY_CALLBACKS). +FlaggedApiLiteral: android.accessibilityservice.AccessibilityService#OVERLAY_RESULT_INVALID: + @FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.view.accessibility.Flags.FLAG_A11Y_OVERLAY_CALLBACKS). +FlaggedApiLiteral: android.accessibilityservice.AccessibilityService#OVERLAY_RESULT_SUCCESS: + @FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.view.accessibility.Flags.FLAG_A11Y_OVERLAY_CALLBACKS). + + InvalidNullabilityOverride: android.app.Notification.TvExtender#extend(android.app.Notification.Builder) parameter #0: Invalid nullability on parameter `builder` in method `extend`. Parameters of overrides cannot be NonNull if the super parameter is unannotated. InvalidNullabilityOverride: android.media.midi.MidiUmpDeviceService#onBind(android.content.Intent) parameter #0: diff --git a/core/api/system-current.txt b/core/api/system-current.txt index a9b181d51fb4..ed95fdd52f45 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -46,6 +46,7 @@ package android { field public static final String ASSOCIATE_COMPANION_DEVICES = "android.permission.ASSOCIATE_COMPANION_DEVICES"; field public static final String BACKGROUND_CAMERA = "android.permission.BACKGROUND_CAMERA"; field public static final String BACKUP = "android.permission.BACKUP"; + field @FlaggedApi("android.permission.flags.health_connect_backup_restore_permission_enabled") public static final String BACKUP_HEALTH_CONNECT_DATA_AND_SETTINGS = "android.permission.BACKUP_HEALTH_CONNECT_DATA_AND_SETTINGS"; field public static final String BATTERY_PREDICTION = "android.permission.BATTERY_PREDICTION"; field public static final String BIND_AMBIENT_CONTEXT_DETECTION_SERVICE = "android.permission.BIND_AMBIENT_CONTEXT_DETECTION_SERVICE"; field public static final String BIND_ATTENTION_SERVICE = "android.permission.BIND_ATTENTION_SERVICE"; @@ -138,6 +139,7 @@ package android { field public static final String DISABLE_SYSTEM_SOUND_EFFECTS = "android.permission.DISABLE_SYSTEM_SOUND_EFFECTS"; field public static final String DISPATCH_PROVISIONING_MESSAGE = "android.permission.DISPATCH_PROVISIONING_MESSAGE"; field public static final String DOMAIN_VERIFICATION_AGENT = "android.permission.DOMAIN_VERIFICATION_AGENT"; + field @FlaggedApi("com.android.art.flags.executable_method_file_offsets") public static final String DYNAMIC_INSTRUMENTATION = "android.permission.DYNAMIC_INSTRUMENTATION"; field @FlaggedApi("com.android.window.flags.untrusted_embedding_any_app_permission") public static final String EMBED_ANY_APP_IN_UNTRUSTED_MODE = "android.permission.EMBED_ANY_APP_IN_UNTRUSTED_MODE"; field @FlaggedApi("android.content.pm.emergency_install_permission") public static final String EMERGENCY_INSTALL_PACKAGES = "android.permission.EMERGENCY_INSTALL_PACKAGES"; field public static final String ENTER_CAR_MODE_PRIORITIZED = "android.permission.ENTER_CAR_MODE_PRIORITIZED"; @@ -346,6 +348,7 @@ package android { field public static final String REQUEST_NOTIFICATION_ASSISTANT_SERVICE = "android.permission.REQUEST_NOTIFICATION_ASSISTANT_SERVICE"; field public static final String RESET_PASSWORD = "android.permission.RESET_PASSWORD"; field public static final String RESTART_WIFI_SUBSYSTEM = "android.permission.RESTART_WIFI_SUBSYSTEM"; + field @FlaggedApi("android.permission.flags.health_connect_backup_restore_permission_enabled") public static final String RESTORE_HEALTH_CONNECT_DATA_AND_SETTINGS = "android.permission.RESTORE_HEALTH_CONNECT_DATA_AND_SETTINGS"; field public static final String RESTORE_RUNTIME_PERMISSIONS = "android.permission.RESTORE_RUNTIME_PERMISSIONS"; field public static final String RESTRICTED_VR_ACCESS = "android.permission.RESTRICTED_VR_ACCESS"; field public static final String RETRIEVE_WINDOW_CONTENT = "android.permission.RETRIEVE_WINDOW_CONTENT"; @@ -706,7 +709,8 @@ package android.app { field public static final String OPSTR_READ_MEDIA_IMAGES = "android:read_media_images"; field public static final String OPSTR_READ_MEDIA_VIDEO = "android:read_media_video"; field public static final String OPSTR_READ_MEDIA_VISUAL_USER_SELECTED = "android:read_media_visual_user_selected"; - field @FlaggedApi("android.permission.flags.platform_skin_temperature_enabled") public static final String OPSTR_READ_SKIN_TEMPERATURE = "android:read_skin_temperature"; + field @FlaggedApi("android.permission.flags.replace_body_sensor_permission_enabled") public static final String OPSTR_READ_OXYGEN_SATURATION = "android:read_oxygen_saturation"; + field @FlaggedApi("android.permission.flags.replace_body_sensor_permission_enabled") public static final String OPSTR_READ_SKIN_TEMPERATURE = "android:read_skin_temperature"; field public static final String OPSTR_READ_WRITE_HEALTH_DATA = "android:read_write_health_data"; field public static final String OPSTR_RECEIVE_AMBIENT_TRIGGER_AUDIO = "android:receive_ambient_trigger_audio"; field public static final String OPSTR_RECEIVE_EMERGENCY_BROADCAST = "android:receive_emergency_broadcast"; @@ -1268,13 +1272,21 @@ package android.app { public class WallpaperManager { method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public void clearWallpaper(int, int); + method @FlaggedApi("android.app.customization_packs_apis") @NonNull @RequiresPermission(android.Manifest.permission.READ_WALLPAPER_INTERNAL) public android.util.SparseArray<android.graphics.Rect> getBitmapCrops(int); + method @FlaggedApi("android.app.customization_packs_apis") public static int getOrientation(@NonNull android.graphics.Point); method @FloatRange(from=0.0f, to=1.0f) @RequiresPermission(android.Manifest.permission.SET_WALLPAPER_DIM_AMOUNT) public float getWallpaperDimAmount(); + method @FlaggedApi("android.app.customization_packs_apis") @Nullable public android.os.ParcelFileDescriptor getWallpaperFile(int, boolean); method @FlaggedApi("android.app.live_wallpaper_content_handling") @Nullable @RequiresPermission(android.Manifest.permission.READ_WALLPAPER_INTERNAL) public android.app.wallpaper.WallpaperInstance getWallpaperInstance(int); method public void setDisplayOffset(android.os.IBinder, int, int); + method @FlaggedApi("com.android.window.flags.multi_crop") @RequiresPermission(android.Manifest.permission.SET_WALLPAPER) public int setStreamWithCrops(@NonNull java.io.InputStream, @NonNull android.util.SparseArray<android.graphics.Rect>, boolean, int) throws java.io.IOException; method @RequiresPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT) public boolean setWallpaperComponent(android.content.ComponentName); method @FlaggedApi("android.app.live_wallpaper_content_handling") @RequiresPermission(allOf={android.Manifest.permission.SET_WALLPAPER_COMPONENT, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}, conditional=true) public boolean setWallpaperComponentWithDescription(@NonNull android.app.wallpaper.WallpaperDescription, int); method @RequiresPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT) public boolean setWallpaperComponentWithFlags(@NonNull android.content.ComponentName, int); method @RequiresPermission(android.Manifest.permission.SET_WALLPAPER_DIM_AMOUNT) public void setWallpaperDimAmount(@FloatRange(from=0.0f, to=1.0f) float); + field @FlaggedApi("android.app.customization_packs_apis") public static final int ORIENTATION_LANDSCAPE = 1; // 0x1 + field @FlaggedApi("android.app.customization_packs_apis") public static final int ORIENTATION_PORTRAIT = 0; // 0x0 + field @FlaggedApi("android.app.customization_packs_apis") public static final int ORIENTATION_SQUARE_LANDSCAPE = 3; // 0x3 + field @FlaggedApi("android.app.customization_packs_apis") public static final int ORIENTATION_SQUARE_PORTRAIT = 2; // 0x2 } } @@ -1356,6 +1368,7 @@ package android.app.admin { method @RequiresPermission("android.permission.NOTIFY_PENDING_SYSTEM_UPDATE") public void notifyPendingSystemUpdate(long, boolean); method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public boolean packageHasActiveAdmins(String); method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS, android.Manifest.permission.PROVISION_DEMO_DEVICE}) public void provisionFullyManagedDevice(@NonNull android.app.admin.FullyManagedDeviceProvisioningParams) throws android.app.admin.ProvisioningException; + method @FlaggedApi("android.app.admin.flags.remove_managed_profile_enabled") @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public boolean removeManagedProfile(); method @RequiresPermission(android.Manifest.permission.TRIGGER_LOST_MODE) public void sendLostModeLocationUpdate(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>); method @Deprecated @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_ADMINS) public boolean setActiveProfileOwner(@NonNull android.content.ComponentName, String) throws java.lang.IllegalArgumentException; method @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_POLICY_APP_EXEMPTIONS) public void setApplicationExemptions(@NonNull String, @NonNull java.util.Set<java.lang.Integer>) throws android.content.pm.PackageManager.NameNotFoundException; @@ -1365,7 +1378,8 @@ package android.app.admin { method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void setDpcDownloaded(boolean); method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void setMaxPolicyStorageLimit(int); method @Deprecated @RequiresPermission(value=android.Manifest.permission.GRANT_PROFILE_OWNER_DEVICE_IDS_ACCESS, conditional=true) public void setProfileOwnerCanAccessDeviceIds(@NonNull android.content.ComponentName); - method public void setSecondaryLockscreenEnabled(@NonNull android.content.ComponentName, boolean); + method @Deprecated @FlaggedApi("android.app.admin.flags.secondary_lockscreen_api_enabled") public void setSecondaryLockscreenEnabled(@NonNull android.content.ComponentName, boolean); + method @FlaggedApi("android.app.admin.flags.secondary_lockscreen_api_enabled") public void setSecondaryLockscreenEnabled(boolean, @Nullable android.os.PersistableBundle); method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void setUserProvisioningState(int, @NonNull android.os.UserHandle); method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public boolean shouldAllowBypassingDevicePolicyManagementRoleQualification(); field public static final String ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_ALLOWED = "android.account.DEVICE_OR_PROFILE_OWNER_ALLOWED"; @@ -8233,6 +8247,26 @@ package android.media.tv { method public void onSetMain(boolean); } + @FlaggedApi("android.media.tv.flags.tif_extension_standardization") public final class TvInputServiceExtensionManager { + method @RequiresPermission(android.Manifest.permission.TV_INPUT_HARDWARE) public int registerExtensionIBinder(@NonNull String, @NonNull android.os.IBinder); + field public static final String IBROADCAST_TIME = "android.media.tv.extension.time.BroadcastTime"; + field public static final String ICAM_APP_INFO_SERVICE = "android.media.tv.extension.cam.ICamAppInfoService"; + field public static final String ICLIENT_TOKEN = "android.media.tv.extension.clienttoken.IClientToken"; + field public static final String IDATA_SERVICE_SIGNAL_INFO = "android.media.tv.extension.teletext.IDataServiceSignalInfo"; + field public static final String IEVENT_MONITOR = "android.media.tv.extension.event.IEventMonitor"; + field public static final String IHDMI_SIGNAL_INTERFACE = "android.media.tv.extension.signal.IHdmiSignalInterface"; + field public static final String IOAD_UPDATE_INTERFACE = "android.media.tv.extension.oad.IOadUpdateInterface"; + field public static final String IRATING_INTERFACE = "android.media.tv.extension.rating.IRatingInterface"; + field public static final String IRECORDED_CONTENTS = "android.media.tv.extension.pvr.IRecordedContents"; + field public static final String ISCAN_INTERFACE = "android.media.tv.extension.scan.IScanInterface"; + field public static final String ISCREEN_MODE_SETTINGS = "android.media.tv.extension.screenmode.IScreenModeSettings"; + field public static final String ISERVICE_LIST_EDIT_LISTENER = "android.media.tv.extension.servicedb.IServiceListEditListener"; + field public static final int REGISTER_FAIL_IMPLEMENTATION_NOT_STANDARDIZED = 2; // 0x2 + field public static final int REGISTER_FAIL_NAME_NOT_STANDARDIZED = 1; // 0x1 + field public static final int REGISTER_FAIL_REMOTE_EXCEPTION = 3; // 0x3 + field public static final int REGISTER_SUCCESS = 0; // 0x0 + } + public abstract static class TvRecordingClient.RecordingCallback { method public void onEvent(String, String, android.os.Bundle); } @@ -12519,8 +12553,19 @@ package android.security.advancedprotection { } @FlaggedApi("android.security.aapm_api") public final class AdvancedProtectionManager { + method @NonNull public android.content.Intent createSupportIntent(@NonNull String, @Nullable String); method @NonNull @RequiresPermission(android.Manifest.permission.SET_ADVANCED_PROTECTION_MODE) public java.util.List<android.security.advancedprotection.AdvancedProtectionFeature> getAdvancedProtectionFeatures(); method @RequiresPermission(android.Manifest.permission.SET_ADVANCED_PROTECTION_MODE) public void setAdvancedProtectionEnabled(boolean); + field @FlaggedApi("android.security.aapm_api") public static final String ACTION_SHOW_ADVANCED_PROTECTION_SUPPORT_DIALOG = "android.security.advancedprotection.action.SHOW_ADVANCED_PROTECTION_SUPPORT_DIALOG"; + field public static final String EXTRA_SUPPORT_DIALOG_FEATURE = "android.security.advancedprotection.extra.SUPPORT_DIALOG_FEATURE"; + field public static final String EXTRA_SUPPORT_DIALOG_TYPE = "android.security.advancedprotection.extra.SUPPORT_DIALOG_TYPE"; + field public static final String FEATURE_ID_DISALLOW_CELLULAR_2G = "android.security.advancedprotection.feature_disallow_2g"; + field public static final String FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES = "android.security.advancedprotection.feature_disallow_install_unknown_sources"; + field public static final String FEATURE_ID_DISALLOW_USB = "android.security.advancedprotection.feature_disallow_usb"; + field public static final String FEATURE_ID_DISALLOW_WEP = "android.security.advancedprotection.feature_disallow_wep"; + field public static final String FEATURE_ID_ENABLE_MTE = "android.security.advancedprotection.feature_enable_mte"; + field public static final String SUPPORT_DIALOG_TYPE_BLOCKED_INTERACTION = "android.security.advancedprotection.type_blocked_interaction"; + field public static final String SUPPORT_DIALOG_TYPE_DISABLED_SETTING = "android.security.advancedprotection.type_disabled_setting"; } } @@ -18331,7 +18376,12 @@ package android.telephony.satellite { method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForModemStateChanged(@NonNull android.telephony.satellite.SatelliteModemStateCallback); method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForNtnSignalStrengthChanged(@NonNull android.telephony.satellite.NtnSignalStrengthCallback); method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForProvisionStateChanged(@NonNull android.telephony.satellite.SatelliteProvisionStateCallback); + field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final int DATAGRAM_TYPE_CHECK_PENDING_INCOMING_SMS = 7; // 0x7 + field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final int DATAGRAM_TYPE_KEEP_ALIVE = 3; // 0x3 + field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final int DATAGRAM_TYPE_LAST_SOS_MESSAGE_NO_HELP_NEEDED = 5; // 0x5 + field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final int DATAGRAM_TYPE_LAST_SOS_MESSAGE_STILL_NEED_HELP = 4; // 0x4 field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int DATAGRAM_TYPE_LOCATION_SHARING = 2; // 0x2 + field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final int DATAGRAM_TYPE_SMS = 6; // 0x6 field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int DATAGRAM_TYPE_SOS_MESSAGE = 1; // 0x1 field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int DATAGRAM_TYPE_UNKNOWN = 0; // 0x0 field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int DEVICE_HOLD_POSITION_LANDSCAPE_LEFT = 2; // 0x2 @@ -18351,6 +18401,7 @@ package android.telephony.satellite { field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int NT_RADIO_TECHNOLOGY_UNKNOWN = 0; // 0x0 field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final int SATELLITE_COMMUNICATION_RESTRICTION_REASON_ENTITLEMENT = 2; // 0x2 field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final int SATELLITE_COMMUNICATION_RESTRICTION_REASON_GEOLOCATION = 1; // 0x1 + field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final int SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER = 0; // 0x0 field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE = 0; // 0x0 field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED = 7; // 0x7 field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_NONE = 6; // 0x6 @@ -18364,6 +18415,8 @@ package android.telephony.satellite { field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_MODEM_STATE_CONNECTED = 7; // 0x7 field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_MODEM_STATE_DATAGRAM_RETRYING = 3; // 0x3 field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING = 2; // 0x2 + field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final int SATELLITE_MODEM_STATE_DISABLING_SATELLITE = 9; // 0x9 + field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final int SATELLITE_MODEM_STATE_ENABLING_SATELLITE = 8; // 0x8 field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_MODEM_STATE_IDLE = 0; // 0x0 field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_MODEM_STATE_LISTENING = 1; // 0x1 field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_MODEM_STATE_NOT_CONNECTED = 6; // 0x6 @@ -18371,11 +18424,16 @@ package android.telephony.satellite { field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_MODEM_STATE_UNAVAILABLE = 5; // 0x5 field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_MODEM_STATE_UNKNOWN = -1; // 0xffffffff field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_RESULT_ACCESS_BARRED = 16; // 0x10 + field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final int SATELLITE_RESULT_DISABLE_IN_PROGRESS = 28; // 0x1c + field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final int SATELLITE_RESULT_EMERGENCY_CALL_IN_PROGRESS = 27; // 0x1b + field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final int SATELLITE_RESULT_ENABLE_IN_PROGRESS = 29; // 0x1d field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_RESULT_ERROR = 1; // 0x1 field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_RESULT_ILLEGAL_STATE = 23; // 0x17 field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_RESULT_INVALID_ARGUMENTS = 8; // 0x8 field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_RESULT_INVALID_MODEM_STATE = 7; // 0x7 field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_RESULT_INVALID_TELEPHONY_STATE = 6; // 0x6 + field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final int SATELLITE_RESULT_LOCATION_DISABLED = 25; // 0x19 + field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final int SATELLITE_RESULT_LOCATION_NOT_AVAILABLE = 26; // 0x1a field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_RESULT_MODEM_BUSY = 22; // 0x16 field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_RESULT_MODEM_ERROR = 4; // 0x4 field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int SATELLITE_RESULT_MODEM_TIMEOUT = 24; // 0x18 @@ -18769,6 +18827,10 @@ package android.webkit { method @Deprecated public void openFileChooser(android.webkit.ValueCallback<android.net.Uri>, String, String); } + public abstract static class WebChromeClient.FileChooserParams { + field @FlaggedApi("android.webkit.file_system_access") public static final long ENABLE_FILE_SYSTEM_ACCESS = 364980165L; // 0x15c127c5L + } + public abstract class WebHistoryItem implements java.lang.Cloneable { method @Deprecated public abstract int getId(); } diff --git a/core/api/system-lint-baseline.txt b/core/api/system-lint-baseline.txt index 78577e2b4090..7c43891f13f2 100644 --- a/core/api/system-lint-baseline.txt +++ b/core/api/system-lint-baseline.txt @@ -501,6 +501,52 @@ DeprecationMismatch: javax.microedition.khronos.egl.EGL10#eglCreatePixmapSurface Method javax.microedition.khronos.egl.EGL10.eglCreatePixmapSurface(javax.microedition.khronos.egl.EGLDisplay, javax.microedition.khronos.egl.EGLConfig, Object, int[]): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match + + +FlaggedApiLiteral: android.Manifest.permission#ACCESS_LAST_KNOWN_CELL_ID: + @FlaggedApi contains a string literal, but should reference the field generated by aconfig (com.android.server.telecom.flags.Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES). +FlaggedApiLiteral: android.Manifest.permission#BACKUP_HEALTH_CONNECT_DATA_AND_SETTINGS: + @FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.permission.flags.Flags.FLAG_HEALTH_CONNECT_BACKUP_RESTORE_PERMISSION_ENABLED). +FlaggedApiLiteral: android.Manifest.permission#BIND_VERIFICATION_AGENT: + @FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.content.pm.Flags.FLAG_VERIFICATION_SERVICE). +FlaggedApiLiteral: android.Manifest.permission#EMBED_ANY_APP_IN_UNTRUSTED_MODE: + @FlaggedApi contains a string literal, but should reference the field generated by aconfig (com.android.window.flags.Flags.FLAG_UNTRUSTED_EMBEDDING_ANY_APP_PERMISSION). +FlaggedApiLiteral: android.Manifest.permission#EXECUTE_APP_FUNCTIONS: + @FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER). +FlaggedApiLiteral: android.Manifest.permission#EXECUTE_APP_FUNCTIONS_TRUSTED: + @FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER). +FlaggedApiLiteral: android.Manifest.permission#MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE: + @FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.media.tv.flags.Flags.FLAG_MEDIA_QUALITY_FW). +FlaggedApiLiteral: android.Manifest.permission#MANAGE_GLOBAL_SOUND_QUALITY_SERVICE: + @FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.media.tv.flags.Flags.FLAG_MEDIA_QUALITY_FW). +FlaggedApiLiteral: android.Manifest.permission#QUARANTINE_APPS: + @FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.content.pm.Flags.FLAG_QUARANTINED_ENABLED). +FlaggedApiLiteral: android.Manifest.permission#QUERY_DEVICE_STOLEN_STATE: + @FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.app.admin.flags.Flags.FLAG_DEVICE_THEFT_API_ENABLED). +FlaggedApiLiteral: android.Manifest.permission#READ_BLOCKED_NUMBERS: + @FlaggedApi contains a string literal, but should reference the field generated by aconfig (com.android.server.telecom.flags.Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES). +FlaggedApiLiteral: android.Manifest.permission#RECEIVE_SANDBOX_TRIGGER_AUDIO: + @FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.permission.flags.Flags.FLAG_VOICE_ACTIVATION_PERMISSION_APIS). +FlaggedApiLiteral: android.Manifest.permission#REGISTER_NSD_OFFLOAD_ENGINE: + @FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.net.platform.flags.Flags.FLAG_REGISTER_NSD_OFFLOAD_ENGINE). +FlaggedApiLiteral: android.Manifest.permission#RESTORE_HEALTH_CONNECT_DATA_AND_SETTINGS: + @FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.permission.flags.Flags.FLAG_HEALTH_CONNECT_BACKUP_RESTORE_PERMISSION_ENABLED). +FlaggedApiLiteral: android.Manifest.permission#SET_ADVANCED_PROTECTION_MODE: + @FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.security.Flags.FLAG_AAPM_API). +FlaggedApiLiteral: android.Manifest.permission#SINGLE_USER_TIS_ACCESS: + @FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.media.tv.flags.Flags.FLAG_KIDS_MODE_TVDB_SHARING). +FlaggedApiLiteral: android.Manifest.permission#THREAD_NETWORK_PRIVILEGED: + @FlaggedApi contains a string literal, but should reference the field generated by aconfig (com.android.net.thread.platform.flags.Flags.FLAG_THREAD_ENABLED_PLATFORM). +FlaggedApiLiteral: android.Manifest.permission#VERIFICATION_AGENT: + @FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.content.pm.Flags.FLAG_VERIFICATION_SERVICE). +FlaggedApiLiteral: android.Manifest.permission#VIBRATE_VENDOR_EFFECTS: + @FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS). +FlaggedApiLiteral: android.Manifest.permission#WRITE_BLOCKED_NUMBERS: + @FlaggedApi contains a string literal, but should reference the field generated by aconfig (com.android.server.telecom.flags.Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES). +FlaggedApiLiteral: android.R.attr#backgroundPermission: + @FlaggedApi contains a string literal, but should reference the field generated by aconfig (android.permission.flags.Flags.FLAG_REPLACE_BODY_SENSOR_PERMISSION_ENABLED). + + GenericException: android.app.prediction.AppPredictor#finalize(): Methods must not throw generic exceptions (`java.lang.Throwable`) GenericException: android.hardware.location.ContextHubClient#finalize(): diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 117351943587..c8ecfa94ec87 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -535,9 +535,13 @@ package android.app { public class WallpaperManager { method @Nullable public android.graphics.Bitmap getBitmap(); method @Nullable public android.graphics.Bitmap getBitmapAsUser(int, boolean, int); + method @FlaggedApi("com.android.window.flags.multi_crop") @NonNull @RequiresPermission(android.Manifest.permission.READ_WALLPAPER_INTERNAL) public java.util.List<android.graphics.Rect> getBitmapCrops(@NonNull java.util.List<android.graphics.Point>, int, boolean); + method @FlaggedApi("com.android.window.flags.multi_crop") @NonNull public java.util.List<android.graphics.Rect> getBitmapCrops(@NonNull android.graphics.Point, @NonNull java.util.List<android.graphics.Point>, @Nullable java.util.Map<android.graphics.Point,android.graphics.Rect>); method public boolean isLockscreenLiveWallpaperEnabled(); 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("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 public void setWallpaperZoomOut(@NonNull android.os.IBinder, float); method public boolean shouldEnableWideColorGamut(); method public boolean wallpaperSupportsWcg(int); @@ -3247,6 +3251,14 @@ package android.service.quicksettings { } +package android.service.settings.preferences { + + @FlaggedApi("com.android.settingslib.flags.settings_catalyst") public class SettingsPreferenceServiceClient implements java.lang.AutoCloseable { + ctor public SettingsPreferenceServiceClient(@NonNull android.content.Context, @NonNull String, boolean, @Nullable android.content.ServiceConnection); + } + +} + package android.service.voice { public class AlwaysOnHotwordDetector implements android.service.voice.HotwordDetector { @@ -3729,7 +3741,7 @@ package android.view { method public final int getDisplayId(); method public final void setDisplayId(int); field public static final int FLAG_IS_ACCESSIBILITY_EVENT = 2048; // 0x800 - field public static final int LAST_KEYCODE = 318; // 0x13e + field public static final int LAST_KEYCODE = 337; // 0x151 } public final class KeyboardShortcutGroup implements android.os.Parcelable { diff --git a/core/api/test-lint-baseline.txt b/core/api/test-lint-baseline.txt index b4a3abc4ad22..08bb08254476 100644 --- a/core/api/test-lint-baseline.txt +++ b/core/api/test-lint-baseline.txt @@ -511,6 +511,12 @@ DeprecationMismatch: javax.microedition.khronos.egl.EGL10#eglCreatePixmapSurface Method javax.microedition.khronos.egl.EGL10.eglCreatePixmapSurface(javax.microedition.khronos.egl.EGLDisplay, javax.microedition.khronos.egl.EGLConfig, Object, int[]): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match +FlaggedApiLiteral: android.Manifest.permission#ACCESSIBILITY_MOTION_EVENT_OBSERVING: + @FlaggedApi contains a string literal, but should reference the field generated by aconfig (com.android.server.accessibility.Flags.FLAG_MOTION_EVENT_OBSERVING, however this flag doesn't seem to exist). +FlaggedApiLiteral: android.net.wifi.sharedconnectivity.app.SharedConnectivityManager#getBroadcastReceiver(): + @FlaggedApi contains a string literal, but should reference the field generated by aconfig (com.android.wifi.flags.Flags.FLAG_SHARED_CONNECTIVITY_BROADCAST_RECEIVER_TEST_API, however this flag doesn't seem to exist). + + InvalidNullabilityOverride: android.window.WindowProviderService#getSystemService(String) parameter #0: Invalid nullability on parameter `name` in method `getSystemService`. Parameters of overrides cannot be NonNull if the super parameter is unannotated. InvalidNullabilityOverride: android.window.WindowProviderService#onConfigurationChanged(android.content.res.Configuration) parameter #0: diff --git a/core/java/Android.bp b/core/java/Android.bp index 71623c566501..cf5ebbaa37b4 100644 --- a/core/java/Android.bp +++ b/core/java/Android.bp @@ -28,6 +28,7 @@ filegroup { exclude_srcs: [ "android/os/*MessageQueue/**/*.java", "android/ranging/**/*.java", + ":dynamic_instrumentation_manager_aidl_sources", ], visibility: ["//frameworks/base"], } @@ -120,6 +121,17 @@ filegroup { } filegroup { + name: "dynamic_instrumentation_manager_aidl_sources", + srcs: ["android/os/instrumentation/*.aidl"], +} + +aidl_interface { + name: "dynamic_instrumentation_manager_aidl", + srcs: [":dynamic_instrumentation_manager_aidl_sources"], + unstable: true, +} + +filegroup { name: "framework-internal-display-sources", srcs: ["com/android/internal/display/BrightnessSynchronizer.java"], visibility: ["//frameworks/base/services/tests/mockingservicestests"], @@ -685,16 +697,31 @@ gen_readonly_feature_apis = select(release_flag("RELEASE_USE_SYSTEM_FEATURE_BUIL // Generates com.android.internal.pm.RoSystemFeatures, optionally compiling in // details about fixed system features defined by build flags. When disabled, // the APIs are simply passthrough stubs with no meaningful side effects. +// TODO(b/203143243): Implement the `--feature=` aggregation directly with a native soong module. genrule { name: "systemfeatures-gen-srcs", cmd: "$(location systemfeatures-gen-tool) com.android.internal.pm.RoSystemFeatures " + // --readonly=false (default) makes the codegen an effective no-op passthrough API. " --readonly=" + gen_readonly_feature_apis + - // For now, only export "android.hardware.type.*" system features APIs. - // TODO(b/203143243): Use an intermediate soong var that aggregates all declared - // RELEASE_SYSTEM_FEATURE_* declarations into a single arg. - " --feature-apis=AUTOMOTIVE,WATCH,TELEVISION,EMBEDDED,PC" + - " > $(out)", + " --feature=AUTOMOTIVE:" + select(release_flag("RELEASE_SYSTEM_FEATURE_AUTOMOTIVE"), { + any @ value: value, + default: "", + }) + " --feature=EMBEDDED:" + select(release_flag("RELEASE_SYSTEM_FEATURE_EMBEDDED"), { + any @ value: value, + default: "", + }) + " --feature=LEANBACK:" + select(release_flag("RELEASE_SYSTEM_FEATURE_LEANBACK"), { + any @ value: value, + default: "", + }) + " --feature=PC:" + select(release_flag("RELEASE_SYSTEM_FEATURE_PC"), { + any @ value: value, + default: "", + }) + " --feature=TELEVISION:" + select(release_flag("RELEASE_SYSTEM_FEATURE_TELEVISION"), { + any @ value: value, + default: "", + }) + " --feature=WATCH:" + select(release_flag("RELEASE_SYSTEM_FEATURE_WATCH"), { + any @ value: value, + default: "", + }) + " > $(out)", out: [ "RoSystemFeatures.java", ], diff --git a/core/java/android/adaptiveauth/OWNERS b/core/java/android/adaptiveauth/OWNERS index 0218a7835586..bc8efa92c16f 100644 --- a/core/java/android/adaptiveauth/OWNERS +++ b/core/java/android/adaptiveauth/OWNERS @@ -1 +1 @@ -include /services/core/java/com/android/server/adaptiveauth/OWNERS
\ No newline at end of file +include /services/core/java/com/android/server/security/adaptiveauthentication/OWNERS
\ No newline at end of file diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 7a1c759a3ec4..419eb7dac5f0 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -30,6 +30,7 @@ import static com.android.sdksandbox.flags.Flags.sandboxActivitySdkBasedContext; import static java.lang.Character.MIN_VALUE; +import android.Manifest; import android.annotation.AnimRes; import android.annotation.CallSuper; import android.annotation.CallbackExecutor; @@ -53,6 +54,7 @@ import android.app.VoiceInteractor.Request; import android.app.admin.DevicePolicyManager; import android.app.assist.AssistContent; import android.app.compat.CompatChanges; +import android.app.jank.JankTracker; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledSince; import android.compat.annotation.UnsupportedAppUsage; @@ -123,6 +125,7 @@ import android.util.Slog; import android.util.SparseArray; import android.util.SuperNotCalledException; import android.view.ActionMode; +import android.view.Choreographer; import android.view.ContextMenu; import android.view.ContextMenu.ContextMenuInfo; import android.view.ContextThemeWrapper; @@ -174,6 +177,7 @@ import com.android.internal.app.IVoiceInteractionManagerService; import com.android.internal.app.IVoiceInteractor; import com.android.internal.app.ToolbarActionBar; import com.android.internal.app.WindowDecorActionBar; +import com.android.internal.policy.DecorView; import com.android.internal.policy.PhoneWindow; import com.android.internal.util.dump.DumpableContainerImpl; @@ -1144,6 +1148,9 @@ public class Activity extends ContextThemeWrapper }; + @Nullable + private JankTracker mJankTracker; + private static native String getDlWarning(); /** @@ -2244,6 +2251,10 @@ public class Activity extends ContextThemeWrapper // Notify autofill getAutofillClientController().onActivityPostResumed(); + if (android.app.jank.Flags.detailedAppJankMetricsApi()) { + startAppJankTracking(); + } + mCalled = true; } @@ -3193,6 +3204,16 @@ public class Activity extends ContextThemeWrapper return ActivityTaskManager.getMaxNumPictureInPictureActions(this); } + private boolean isImplicitEnterPipProhibited() { + PackageManager pm = getPackageManager(); + if (android.app.Flags.enableTvImplicitEnterPipRestriction()) { + return pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK) + && pm.checkPermission(Manifest.permission.TV_IMPLICIT_ENTER_PIP, + getPackageName()) == PackageManager.PERMISSION_DENIED; + } + return false; + } + /** * @return Whether this device supports picture-in-picture. */ @@ -9192,6 +9213,8 @@ public class Activity extends ContextThemeWrapper } dispatchActivityPreResumed(); + mCanEnterPictureInPicture = true; + mFragments.execPendingActions(); mLastNonConfigurationInstances = null; @@ -9243,9 +9266,17 @@ public class Activity extends ContextThemeWrapper Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "performPause:" + mComponent.getClassName()); } + + if (isImplicitEnterPipProhibited()) { + mCanEnterPictureInPicture = false; + } + dispatchActivityPrePaused(); mDoReportFullyDrawn = false; mFragments.dispatchPause(); + if (android.app.jank.Flags.detailedAppJankMetricsApi()) { + stopAppJankTracking(); + } mCalled = false; final long startTime = SystemClock.uptimeMillis(); onPause(); @@ -9265,6 +9296,10 @@ public class Activity extends ContextThemeWrapper final void performUserLeaving() { onUserInteraction(); + + if (isImplicitEnterPipProhibited()) { + mCanEnterPictureInPicture = false; + } onUserLeaveHint(); } @@ -9924,4 +9959,49 @@ public class Activity extends ContextThemeWrapper mScreenCaptureCallbackHandler.unregisterScreenCaptureCallback(callback); } } + + /** + * Enabling jank tracking for this activity but only if certain conditions are met. The + * application must have an app category other than undefined and a visible view. + */ + private void startAppJankTracking() { + if (!android.app.jank.Flags.detailedAppJankMetricsLoggingEnabled()) { + return; + } + if (mApplication.getApplicationInfo().category == ApplicationInfo.CATEGORY_UNDEFINED) { + return; + } + if (getWindow() != null && getWindow().peekDecorView() != null) { + DecorView decorView = (DecorView) getWindow().peekDecorView(); + if (decorView.getVisibility() == View.VISIBLE) { + decorView.setAppJankStatsCallback(new DecorView.AppJankStatsCallback() { + @Override + public JankTracker getAppJankTracker() { + return mJankTracker; + } + }); + if (mJankTracker == null) { + // TODO b/377960907 use the Choreographer attached to the ViewRootImpl instead. + mJankTracker = new JankTracker(Choreographer.getInstance(), + decorView); + } + // TODO b/377674765 confirm this is the string we want logged. + mJankTracker.setActivityName(getComponentName().getClassName()); + mJankTracker.setAppUid(myUid()); + mJankTracker.enableAppJankTracking(); + } + } + } + + /** + * Call to disable jank tracking for this activity. + */ + private void stopAppJankTracking() { + if (!android.app.jank.Flags.detailedAppJankMetricsLoggingEnabled()) { + return; + } + if (mJankTracker != null) { + mJankTracker.disableAppJankTracking(); + } + } } diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index b447897733e1..1b707f79ab81 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -1017,6 +1017,12 @@ public class ActivityManager { public static final int PROCESS_CAPABILITY_FOREGROUND_AUDIO_CONTROL = 1 << 6; /** + * @hide + * Process is guaranteed cpu time (IE. it will not be frozen). + */ + public static final int PROCESS_CAPABILITY_CPU_TIME = 1 << 7; + + /** * @hide all capabilities, the ORing of all flags in {@link ProcessCapability}. * * Don't expose it as TestApi -- we may add new capabilities any time, which could @@ -1028,7 +1034,8 @@ public class ActivityManager { | PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK | PROCESS_CAPABILITY_BFSL | PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK - | PROCESS_CAPABILITY_FOREGROUND_AUDIO_CONTROL; + | PROCESS_CAPABILITY_FOREGROUND_AUDIO_CONTROL + | PROCESS_CAPABILITY_CPU_TIME; /** * All implicit capabilities. This capability set is currently only used for processes under @@ -1053,6 +1060,7 @@ public class ActivityManager { pw.print((caps & PROCESS_CAPABILITY_BFSL) != 0 ? 'F' : '-'); pw.print((caps & PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK) != 0 ? 'U' : '-'); pw.print((caps & PROCESS_CAPABILITY_FOREGROUND_AUDIO_CONTROL) != 0 ? 'A' : '-'); + pw.print((caps & PROCESS_CAPABILITY_CPU_TIME) != 0 ? 'T' : '-'); } /** @hide */ @@ -1065,6 +1073,7 @@ public class ActivityManager { sb.append((caps & PROCESS_CAPABILITY_BFSL) != 0 ? 'F' : '-'); sb.append((caps & PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK) != 0 ? 'U' : '-'); sb.append((caps & PROCESS_CAPABILITY_FOREGROUND_AUDIO_CONTROL) != 0 ? 'A' : '-'); + sb.append((caps & PROCESS_CAPABILITY_CPU_TIME) != 0 ? 'T' : '-'); } /** diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 38c8583dd024..8b37dbd04bec 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -1621,9 +1621,12 @@ public class AppOpsManager { */ public static final int OP_RANGING = AppOpEnums.APP_OP_RANGING; + /** @hide Access to read oxygen saturation. */ + public static final int OP_READ_OXYGEN_SATURATION = AppOpEnums.APP_OP_READ_OXYGEN_SATURATION; + /** @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public static final int _NUM_OP = 152; + public static final int _NUM_OP = 153; /** * All app ops represented as strings. @@ -1779,6 +1782,7 @@ public class AppOpsManager { OPSTR_READ_HEART_RATE, OPSTR_READ_SKIN_TEMPERATURE, OPSTR_RANGING, + OPSTR_READ_OXYGEN_SATURATION, }) public @interface AppOpString {} @@ -2521,9 +2525,14 @@ public class AppOpsManager { @FlaggedApi(Flags.FLAG_REPLACE_BODY_SENSOR_PERMISSION_ENABLED) public static final String OPSTR_READ_HEART_RATE = "android:read_heart_rate"; + /** @hide Access to read oxygen saturation. */ + @SystemApi + @FlaggedApi(Flags.FLAG_REPLACE_BODY_SENSOR_PERMISSION_ENABLED) + public static final String OPSTR_READ_OXYGEN_SATURATION = "android:read_oxygen_saturation"; + /** @hide Access to read skin temperature. */ @SystemApi - @FlaggedApi(Flags.FLAG_PLATFORM_SKIN_TEMPERATURE_ENABLED) + @FlaggedApi(Flags.FLAG_REPLACE_BODY_SENSOR_PERMISSION_ENABLED) public static final String OPSTR_READ_SKIN_TEMPERATURE = "android:read_skin_temperature"; /** @hide Access to ranging */ @@ -2607,7 +2616,8 @@ public class AppOpsManager { OP_POST_NOTIFICATION, // Health Flags.replaceBodySensorPermissionEnabled() ? OP_READ_HEART_RATE : OP_NONE, - Flags.platformSkinTemperatureEnabled() ? OP_READ_SKIN_TEMPERATURE : OP_NONE, + Flags.replaceBodySensorPermissionEnabled() ? OP_READ_SKIN_TEMPERATURE : OP_NONE, + Flags.replaceBodySensorPermissionEnabled() ? OP_READ_OXYGEN_SATURATION : OP_NONE, }; /** @@ -3122,13 +3132,18 @@ public class AppOpsManager { .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(), new AppOpInfo.Builder(OP_READ_SKIN_TEMPERATURE, OPSTR_READ_SKIN_TEMPERATURE, "READ_SKIN_TEMPERATURE").setPermission( - Flags.platformSkinTemperatureEnabled() + Flags.replaceBodySensorPermissionEnabled() ? HealthPermissions.READ_SKIN_TEMPERATURE : null) .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(), new AppOpInfo.Builder(OP_RANGING, OPSTR_RANGING, "RANGING") .setPermission(Flags.rangingPermissionEnabled()? Manifest.permission.RANGING : null) .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(), + new AppOpInfo.Builder(OP_READ_OXYGEN_SATURATION, OPSTR_READ_OXYGEN_SATURATION, + "READ_OXYGEN_SATURATION").setPermission( + Flags.replaceBodySensorPermissionEnabled() + ? HealthPermissions.READ_OXYGEN_SATURATION : null) + .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(), }; // The number of longs needed to form a full bitmask of app ops diff --git a/core/java/android/app/DisabledWallpaperManager.java b/core/java/android/app/DisabledWallpaperManager.java index b06fb9e2f284..233dc75b810f 100644 --- a/core/java/android/app/DisabledWallpaperManager.java +++ b/core/java/android/app/DisabledWallpaperManager.java @@ -177,6 +177,13 @@ final class DisabledWallpaperManager extends WallpaperManager { } @Override + @NonNull + public SparseArray<Rect> getBitmapCrops(int which) { + unsupported(); + return new SparseArray<>(); + } + + @Override public List<Rect> getBitmapCrops(@NonNull Point bitmapSize, @NonNull List<Point> displaySizes, @Nullable Map<Point, Rect> cropHints) { return unsupported(); @@ -358,8 +365,9 @@ final class DisabledWallpaperManager extends WallpaperManager { @Override - public int setStreamWithCrops(InputStream bitmapData, @NonNull SparseArray<Rect> cropHints, - boolean allowBackup, @SetWallpaperFlags int which) throws IOException { + public int setStreamWithCrops(@NonNull InputStream bitmapData, + @NonNull SparseArray<Rect> cropHints, boolean allowBackup, @SetWallpaperFlags int which + ) throws IOException { return unsupportedInt(); } diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index 0654ac2f33ce..9bb16ae7fa02 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -124,7 +124,7 @@ interface INotificationManager boolean onlyHasDefaultChannel(String pkg, int uid); boolean areChannelsBypassingDnd(); ParceledListSlice getNotificationChannelsBypassingDnd(String pkg, int uid); - List<String> getPackagesBypassingDnd(int userId, boolean includeConversationChannels); + ParceledListSlice getPackagesBypassingDnd(int userId); boolean isPackagePaused(String pkg); void deleteNotificationHistoryItem(String pkg, int uid, long postedTime); boolean isPermissionFixed(String pkg, int userId); diff --git a/core/java/android/app/IWallpaperManager.aidl b/core/java/android/app/IWallpaperManager.aidl index f693e9ba11ec..6449ea1742a1 100644 --- a/core/java/android/app/IWallpaperManager.aidl +++ b/core/java/android/app/IWallpaperManager.aidl @@ -97,6 +97,16 @@ interface IWallpaperManager { List getBitmapCrops(in List<Point> displaySizes, int which, boolean originalBitmap, int userId); /** + * For a given user, if the wallpaper of the specified which is an ImageWallpaper, return + * a bundle which is a Map<Integer, Rect> containing the custom cropHints that were sent to + * setBitmapWithCrops or setStreamWithCrops. These crops are relative to the original bitmap. + * If the wallpaper isn't an ImageWallpaper, return null. + */ + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.READ_WALLPAPER_INTERNAL)") + @SuppressWarnings(value={"untyped-collection"}) + Bundle getCurrentBitmapCrops(int which, int userId); + + /** * Return how a bitmap of a given size would be cropped for a given list of display sizes when * set with the given suggested crops. * @hide diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 0381ee0e25ac..3d9c55c0f37a 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -5003,7 +5003,7 @@ public class Notification implements Parcelable /** * Sets a very short string summarizing the most critical information contained in the - * notification. Suggested max length is 5 characters, and there is no guarantee how much or + * notification. Suggested max length is 7 characters, and there is no guarantee how much or * how little of this text will be shown. */ @FlaggedApi(Flags.FLAG_API_RICH_ONGOING) diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java index f432a22b14ac..1dc774285a32 100644 --- a/core/java/android/app/PropertyInvalidatedCache.java +++ b/core/java/android/app/PropertyInvalidatedCache.java @@ -32,7 +32,10 @@ import android.os.Process; import android.os.SystemClock; import android.os.SystemProperties; import android.util.ArrayMap; +import android.util.ArraySet; import android.util.Log; +import android.util.SparseArray; +import android.util.SparseBooleanArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; @@ -92,9 +95,6 @@ public class PropertyInvalidatedCache<Query, Result> { * caching on behalf of other processes. */ public boolean shouldBypassCache(@NonNull Q query) { - if(android.multiuser.Flags.propertyInvalidatedCacheBypassMismatchedUids()) { - return Binder.getCallingUid() != Process.myUid(); - } return false; } }; @@ -392,8 +392,213 @@ public class PropertyInvalidatedCache<Query, Result> { } } + /** + * An array of hash maps, indexed by calling UID. The class behaves a bit like a hash map + * except that it uses the calling UID internally. + */ + private class CacheMap<Query, Result> { + + // Create a new map for a UID, using the parent's configuration for max size. + private LinkedHashMap<Query, Result> createMap() { + return new LinkedHashMap<Query, Result>( + 2 /* start small */, + 0.75f /* default load factor */, + true /* LRU access order */) { + @GuardedBy("mLock") + @Override + protected boolean removeEldestEntry(Map.Entry eldest) { + final int size = size(); + if (size > mHighWaterMark) { + mHighWaterMark = size; + } + if (size > mMaxEntries) { + mMissOverflow++; + return true; + } + return false; + } + }; + } + + // An array of maps, indexed by UID. + private final SparseArray<LinkedHashMap<Query, Result>> mCache = new SparseArray<>(); + + // If true, isolate the hash entries by calling UID. If this is false, allow the cache + // entries to be combined in a single hash map. + private final boolean mIsolated; + + // Collect statistics. + private final boolean mStatistics; + + // An array of booleans to indicate if a UID has been involved in a map access. A value + // exists for every UID that was ever involved during cache access. This is updated only + // if statistics are being collected. + private final SparseBooleanArray mUidSeen; + + // A hash map that ignores the UID. This is used in look-aside fashion just for hit/miss + // statistics. This is updated only if statistics are being collected. + private final ArraySet<Query> mShadowCache; + + // Shadow statistics. Only hits and misses need to be recorded. These are updated only + // if statistics are being collected. The "SelfHits" records hits when the UID is the + // process uid. + private int mShadowHits; + private int mShadowMisses; + private int mShadowSelfHits; + + // The process UID. + private final int mSelfUid; + + // True in test mode. In test mode, the cache uses Binder.getWorkSource() as the UID. + private final boolean mTestMode; + + /** + * Create a CacheMap. UID isolation is enabled if the input parameter is true and if the + * isolation feature is enabled. + */ + CacheMap(boolean isolate, boolean testMode) { + mIsolated = Flags.picIsolateCacheByUid() && isolate; + mStatistics = Flags.picIsolatedCacheStatistics() && mIsolated; + if (mStatistics) { + mUidSeen = new SparseBooleanArray(); + mShadowCache = new ArraySet<>(); + } else { + mUidSeen = null; + mShadowCache = null; + } + mSelfUid = Process.myUid(); + mTestMode = testMode; + } + + // Return the UID for this cache invocation. If uid isolation is disabled, the value of 0 + // is returned, which effectively places all entries in a single hash map. + private int callerUid() { + if (!mIsolated) { + return 0; + } else if (mTestMode) { + return Binder.getCallingWorkSourceUid(); + } else { + return Binder.getCallingUid(); + } + } + + /** + * Lookup an entry in the cache. + */ + Result get(Query query) { + final int uid = callerUid(); + + // Shadow statistics + if (mStatistics) { + if (mShadowCache.contains(query)) { + mShadowHits++; + if (uid == mSelfUid) { + mShadowSelfHits++; + } + } else { + mShadowMisses++; + } + } + + var map = mCache.get(uid); + if (map != null) { + return map.get(query); + } else { + return null; + } + } + + /** + * Remove an entry from the cache. + */ + void remove(Query query) { + final int uid = callerUid(); + if (mStatistics) { + mShadowCache.remove(query); + } + + var map = mCache.get(uid); + if (map != null) { + map.remove(query); + } + } + + /** + * Record an entry in the cache. + */ + void put(Query query, Result result) { + final int uid = callerUid(); + if (mStatistics) { + mShadowCache.add(query); + mUidSeen.put(uid, true); + } + + var map = mCache.get(uid); + if (map == null) { + map = createMap(); + mCache.put(uid, map); + } + map.put(query, result); + } + + /** + * Return the number of entries in the cache. + */ + int size() { + int total = 0; + for (int i = 0; i < mCache.size(); i++) { + var map = mCache.valueAt(i); + total += map.size(); + } + return total; + } + + /** + * Clear the entries in the cache. Update the shadow statistics. + */ + void clear() { + if (mStatistics) { + mShadowCache.clear(); + } + + mCache.clear(); + } + + // Dump basic statistics, if any are collected. Do nothing if statistics are not enabled. + void dump(PrintWriter pw) { + if (mStatistics) { + pw.println(formatSimple(" ShadowHits: %d, ShadowMisses: %d, ShadowSize: %d", + mShadowHits, mShadowMisses, mShadowCache.size())); + pw.println(formatSimple(" ShadowUids: %d, SelfUid: %d", + mUidSeen.size(), mShadowSelfHits)); + } + } + + // Dump detailed statistics + void dumpDetailed(PrintWriter pw) { + for (int i = 0; i < mCache.size(); i++) { + int uid = mCache.keyAt(i); + var map = mCache.valueAt(i); + + Set<Map.Entry<Query, Result>> cacheEntries = map.entrySet(); + if (cacheEntries.size() == 0) { + break; + } + + pw.println(" Contents:"); + pw.println(formatSimple(" Uid: %d\n", uid)); + for (Map.Entry<Query, Result> entry : cacheEntries) { + String key = Objects.toString(entry.getKey()); + String value = Objects.toString(entry.getValue()); + + pw.println(formatSimple(" Key: %s\n Value: %s\n", key, value)); + } + } + } + } + @GuardedBy("mLock") - private final LinkedHashMap<Query, Result> mCache; + private final CacheMap<Query, Result> mCache; /** * The nonce handler for this cache. @@ -895,7 +1100,8 @@ public class PropertyInvalidatedCache<Query, Result> { * is allowed to be null in the record constructor to facility reuse of Args instances. * @hide */ - public static record Args(@NonNull String mModule, @Nullable String mApi, int mMaxEntries) { + public static record Args(@NonNull String mModule, @Nullable String mApi, + int mMaxEntries, boolean mIsolateUids, boolean mTestMode) { // Validation: the module must be one of the known module strings and the maxEntries must // be positive. @@ -909,15 +1115,28 @@ public class PropertyInvalidatedCache<Query, Result> { // which is not legal, but there is no reasonable default. Clients must call the api // method to set the field properly. public Args(@NonNull String module) { - this(module, /* api */ null, /* maxEntries */ 32); + this(module, + null, // api + 32, // maxEntries + true, // isolateUids + false // testMode + ); } public Args api(@NonNull String api) { - return new Args(mModule, api, mMaxEntries); + return new Args(mModule, api, mMaxEntries, mIsolateUids, mTestMode); } public Args maxEntries(int val) { - return new Args(mModule, mApi, val); + return new Args(mModule, mApi, val, mIsolateUids, mTestMode); + } + + public Args isolateUids(boolean val) { + return new Args(mModule, mApi, mMaxEntries, val, mTestMode); + } + + public Args testMode(boolean val) { + return new Args(mModule, mApi, mMaxEntries, mIsolateUids, val); } } @@ -936,7 +1155,7 @@ public class PropertyInvalidatedCache<Query, Result> { mCacheName = cacheName; mNonce = getNonceHandler(mPropertyName); mMaxEntries = args.mMaxEntries; - mCache = createMap(); + mCache = new CacheMap<>(args.mIsolateUids, args.mTestMode); mComputer = (computer != null) ? computer : new DefaultComputer<>(this); registerCache(); } @@ -1006,28 +1225,6 @@ public class PropertyInvalidatedCache<Query, Result> { this(new Args(module).maxEntries(maxEntries).api(api), cacheName, computer); } - // Create a map. This should be called only from the constructor. - private LinkedHashMap<Query, Result> createMap() { - return new LinkedHashMap<Query, Result>( - 2 /* start small */, - 0.75f /* default load factor */, - true /* LRU access order */) { - @GuardedBy("mLock") - @Override - protected boolean removeEldestEntry(Map.Entry eldest) { - final int size = size(); - if (size > mHighWaterMark) { - mHighWaterMark = size; - } - if (size > mMaxEntries) { - mMissOverflow++; - return true; - } - return false; - } - }; - } - /** * Register the map in the global list. If the cache is disabled globally, disable it * now. This method is only ever called from the constructor, which means no other thread has @@ -1778,8 +1975,8 @@ public class PropertyInvalidatedCache<Query, Result> { pw.println(formatSimple(" Cache Name: %s", cacheName())); pw.println(formatSimple(" Property: %s", mPropertyName)); pw.println(formatSimple( - " Hits: %d, Misses: %d, Skips: %d, Clears: %d", - mHits, mMisses, getSkipsLocked(), mClears)); + " Hits: %d, Misses: %d, Skips: %d, Clears: %d, Uids: %d", + mHits, mMisses, getSkipsLocked(), mClears, mCache.size())); // Print all the skip reasons. pw.format(" Skip-%s: %d", sNonceName[0], mSkips[0]); @@ -1794,25 +1991,16 @@ public class PropertyInvalidatedCache<Query, Result> { pw.println(formatSimple( " Current Size: %d, Max Size: %d, HW Mark: %d, Overflows: %d", mCache.size(), mMaxEntries, mHighWaterMark, mMissOverflow)); + mCache.dump(pw); pw.println(formatSimple(" Enabled: %s", mDisabled ? "false" : "true")); - // No specific cache was requested. This is the default, and no details - // should be dumped. - if (!detailed) { - return; - } - Set<Map.Entry<Query, Result>> cacheEntries = mCache.entrySet(); - if (cacheEntries.size() == 0) { - return; + // Dump the contents of the cache. + if (detailed) { + mCache.dumpDetailed(pw); } - pw.println(" Contents:"); - for (Map.Entry<Query, Result> entry : cacheEntries) { - String key = Objects.toString(entry.getKey()); - String value = Objects.toString(entry.getValue()); - - pw.println(formatSimple(" Key: %s\n Value: %s\n", key, value)); - } + // Separator between caches. + pw.println(""); } } diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java index a458b4e45796..f702b85bfcbb 100644 --- a/core/java/android/app/ResourcesManager.java +++ b/core/java/android/app/ResourcesManager.java @@ -174,22 +174,54 @@ public class ResourcesManager { } /** - * Apply the registered library paths to the passed impl object - * @return the hash code for the current version of the registered paths + * Apply the registered library paths to the passed AssetManager. If may create a new + * AssetManager if any changes are needed and it isn't allowed to reuse the old one. + * + * @return new AssetManager and the hash code for the current version of the registered paths */ - public int updateResourceImplWithRegisteredLibs(@NonNull ResourcesImpl impl) { + public @NonNull Pair<AssetManager, Integer> updateResourceImplAssetsWithRegisteredLibs( + @NonNull AssetManager assets, boolean reuseAssets) { if (!Flags.registerResourcePaths()) { - return 0; + return new Pair<>(assets, 0); } - final var collector = new PathCollector(null); - final int size = mSharedLibAssetsMap.size(); - for (int i = 0; i < size; i++) { - final var libraryKey = mSharedLibAssetsMap.valueAt(i).getResourcesKey(); - collector.appendKey(libraryKey); + final int size; + final PathCollector collector; + + synchronized (mLock) { + size = mSharedLibAssetsMap.size(); + if (assets == AssetManager.getSystem()) { + return new Pair<>(assets, size); + } + collector = new PathCollector(resourcesKeyFromAssets(assets)); + for (int i = 0; i < size; i++) { + final var libraryKey = mSharedLibAssetsMap.valueAt(i).getResourcesKey(); + collector.appendKey(libraryKey); + } + } + if (collector.isSameAsOriginal()) { + return new Pair<>(assets, size); + } + if (reuseAssets) { + assets.addPresetApkKeys(extractApkKeys(collector.collectedKey())); + return new Pair<>(assets, size); + } + final var newAssetsBuilder = new AssetManager.Builder(); + for (final var asset : assets.getApkAssets()) { + if (!asset.isForLoader()) { + newAssetsBuilder.addApkAssets(asset); + } } - impl.getAssets().addPresetApkKeys(extractApkKeys(collector.collectedKey())); - return size; + for (final var key : extractApkKeys(collector.collectedKey())) { + try { + final var asset = loadApkAssets(key); + newAssetsBuilder.addApkAssets(asset); + } catch (IOException e) { + Log.e(TAG, "Couldn't load assets for key " + key, e); + } + } + assets.getLoaders().forEach(newAssetsBuilder::addLoader); + return new Pair<>(newAssetsBuilder.build(), size); } public static class ApkKey { @@ -624,6 +656,23 @@ public class ResourcesManager { return apkKeys; } + private ResourcesKey resourcesKeyFromAssets(@NonNull AssetManager assets) { + final var libs = new ArrayList<String>(); + final var overlays = new ArrayList<String>(); + for (final ApkAssets asset : assets.getApkAssets()) { + if (asset.isSystem() || asset.isForLoader()) { + continue; + } + if (asset.isOverlay()) { + overlays.add(asset.getAssetPath()); + } else if (asset.isSharedLib()) { + libs.add(asset.getAssetPath()); + } + } + return new ResourcesKey(null, null, overlays.toArray(new String[0]), + libs.toArray(new String[0]), 0, null, null); + } + /** * Creates an AssetManager from the paths within the ResourcesKey. * @@ -752,7 +801,7 @@ public class ResourcesManager { final Configuration config = generateConfig(key); final DisplayMetrics displayMetrics = getDisplayMetrics(generateDisplayId(key), daj); - final ResourcesImpl impl = new ResourcesImpl(assets, displayMetrics, config, daj); + final ResourcesImpl impl = new ResourcesImpl(assets, displayMetrics, config, daj, true); if (DEBUG) { Slog.d(TAG, "- creating impl=" + impl + " with key: " + key); @@ -1835,31 +1884,32 @@ public class ResourcesManager { for (int i = 0; i < resourcesCount; i++) { final WeakReference<Resources> ref = mAllResourceReferences.get(i); final Resources r = ref != null ? ref.get() : null; - if (r != null) { - final ResourcesKey key = updatedResourceKeys.get(r.getImpl()); - if (key != null) { - final ResourcesImpl impl = findOrCreateResourcesImplForKeyLocked(key); - if (impl == null) { - throw new Resources.NotFoundException("failed to redirect ResourcesImpl"); - } - r.setImpl(impl); - } else { - // ResourcesKey is null which means the ResourcesImpl could belong to a - // Resources created by application through Resources constructor and was not - // managed by ResourcesManager, so the ResourcesImpl needs to be recreated to - // have shared library asset paths appended if there are any. - if (r.getImpl() != null) { - final ResourcesImpl oldImpl = r.getImpl(); - final AssetManager oldAssets = oldImpl.getAssets(); - // ResourcesImpl constructor will help to append shared library asset paths. - if (oldAssets != AssetManager.getSystem() && oldAssets.isUpToDate()) { - final ResourcesImpl newImpl = new ResourcesImpl(oldAssets, - oldImpl.getMetrics(), oldImpl.getConfiguration(), - oldImpl.getDisplayAdjustments()); + if (r == null) { + continue; + } + final ResourcesKey key = updatedResourceKeys.get(r.getImpl()); + if (key != null) { + final ResourcesImpl impl = findOrCreateResourcesImplForKeyLocked(key); + if (impl == null) { + throw new Resources.NotFoundException("failed to redirect ResourcesImpl"); + } + r.setImpl(impl); + } else { + // ResourcesKey is null which means the ResourcesImpl could belong to a + // Resources created by application through Resources constructor and was not + // managed by ResourcesManager, so the ResourcesImpl needs to be recreated to + // have shared library asset paths appended if there are any. + final ResourcesImpl oldImpl = r.getImpl(); + if (oldImpl != null) { + final AssetManager oldAssets = oldImpl.getAssets(); + // ResourcesImpl constructor will help to append shared library asset paths. + if (oldAssets != AssetManager.getSystem()) { + if (oldAssets.isUpToDate()) { + final ResourcesImpl newImpl = new ResourcesImpl(oldImpl); r.setImpl(newImpl); } else { - Slog.w(TAG, "Skip appending shared library asset paths for the " - + "Resource as its assets are not up to date."); + Slog.w(TAG, "Skip appending shared library asset paths for " + + "the Resources as its assets are not up to date."); } } } diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java index 479f3df9affb..abb2dd465576 100644 --- a/core/java/android/app/WallpaperManager.java +++ b/core/java/android/app/WallpaperManager.java @@ -19,9 +19,10 @@ package android.app; import static android.Manifest.permission.MANAGE_EXTERNAL_STORAGE; import static android.Manifest.permission.READ_WALLPAPER_INTERNAL; import static android.Manifest.permission.SET_WALLPAPER_DIM_AMOUNT; +import static android.app.Flags.FLAG_CUSTOMIZATION_PACKS_APIS; +import static android.app.Flags.FLAG_LIVE_WALLPAPER_CONTENT_HANDLING; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.os.ParcelFileDescriptor.MODE_READ_ONLY; -import static android.app.Flags.FLAG_LIVE_WALLPAPER_CONTENT_HANDLING; import static com.android.window.flags.Flags.FLAG_MULTI_CROP; import static com.android.window.flags.Flags.multiCrop; @@ -342,24 +343,32 @@ public class WallpaperManager { * Portrait orientation of most screens * @hide */ + @FlaggedApi(FLAG_CUSTOMIZATION_PACKS_APIS) + @SystemApi public static final int ORIENTATION_PORTRAIT = 0; /** * Landscape orientation of most screens * @hide */ + @FlaggedApi(FLAG_CUSTOMIZATION_PACKS_APIS) + @SystemApi public static final int ORIENTATION_LANDSCAPE = 1; /** * Portrait orientation with similar width and height (e.g. the inner screen of a foldable) * @hide */ + @FlaggedApi(FLAG_CUSTOMIZATION_PACKS_APIS) + @SystemApi public static final int ORIENTATION_SQUARE_PORTRAIT = 2; /** * Landscape orientation with similar width and height (e.g. the inner screen of a foldable) * @hide */ + @FlaggedApi(FLAG_CUSTOMIZATION_PACKS_APIS) + @SystemApi public static final int ORIENTATION_SQUARE_LANDSCAPE = 3; /** @@ -368,7 +377,9 @@ public class WallpaperManager { * @return the corresponding {@link ScreenOrientation}. * @hide */ - public static @ScreenOrientation int getOrientation(Point screenSize) { + @FlaggedApi(FLAG_CUSTOMIZATION_PACKS_APIS) + @SystemApi + public static @ScreenOrientation int getOrientation(@NonNull Point screenSize) { float ratio = ((float) screenSize.x) / screenSize.y; // ratios between 3/4 and 4/3 are considered square return ratio >= 4 / 3f ? ORIENTATION_LANDSCAPE @@ -1623,14 +1634,15 @@ public class WallpaperManager { * If false, return areas relative to the cropped bitmap. * @return A List of Rect where the Rect is within the cropped/original bitmap, and corresponds * to what is displayed. The Rect may have a larger width/height ratio than the screen - * due to parallax. Return {@code null} if the wallpaper is not an ImageWallpaper. - * Also return {@code null} when called with which={@link #FLAG_LOCK} if there is a + * due to parallax. Return an empty list if the wallpaper is not an ImageWallpaper. + * Also return an empty list when called with which={@link #FLAG_LOCK} if there is a * shared home + lock wallpaper. * @hide */ @FlaggedApi(FLAG_MULTI_CROP) + @TestApi @RequiresPermission(READ_WALLPAPER_INTERNAL) - @Nullable + @NonNull public List<Rect> getBitmapCrops(@NonNull List<Point> displaySizes, @SetWallpaperFlags int which, boolean originalBitmap) { checkExactlyOneWallpaperFlagSet(which); @@ -1653,6 +1665,52 @@ public class WallpaperManager { } /** + * For the current user, if the wallpaper of the specified destination is an ImageWallpaper, + * return the custom crops of the wallpaper, that have been provided for example via + * {@link #setStreamWithCrops}. These crops are relative to the original bitmap. + * <p> + * This method helps apps that change wallpapers provide an undo option. Calling + * {@link #setStreamWithCrops(InputStream, SparseArray, boolean, int)} with this SparseArray and + * the current original bitmap file, that can be obtained with {@link #getWallpaperFile(int, + * boolean)} with {@code getCropped=false}, will exactly lead to the current wallpaper state. + * + * @param which wallpaper type. Must be either {@link #FLAG_SYSTEM} or {@link #FLAG_LOCK}. + * @return A map from {{@link #ORIENTATION_PORTRAIT}, {@link #ORIENTATION_LANDSCAPE}, + * {@link #ORIENTATION_SQUARE_PORTRAIT}, {{@link #ORIENTATION_SQUARE_LANDSCAPE}}} to + * Rect, representing the custom cropHints. The map can be empty and will only contains + * entries for screen orientations for which a custom crop was provided. If no custom + * crop is provided for an orientation, the system will infer the crop based on the + * custom crops of the other orientations; or center-align the full image if no custom + * crops are provided at all. + * <p> + * Return an empty map if the wallpaper is not an ImageWallpaper. Also return + * an empty map when called with which={@link #FLAG_LOCK} if there is a shared + * home + lock wallpaper. + * + * @hide + */ + @FlaggedApi(FLAG_CUSTOMIZATION_PACKS_APIS) + @SystemApi + @RequiresPermission(READ_WALLPAPER_INTERNAL) + @NonNull + public SparseArray<Rect> getBitmapCrops(@SetWallpaperFlags int which) { + checkExactlyOneWallpaperFlagSet(which); + try { + Bundle bundle = sGlobals.mService.getCurrentBitmapCrops(which, mContext.getUserId()); + SparseArray<Rect> result = new SparseArray<>(); + if (bundle == null) return result; + for (String key : bundle.keySet()) { + int intKey = Integer.parseInt(key); + Rect rect = bundle.getParcelable(key, Rect.class); + result.put(intKey, rect); + } + return result; + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * For preview purposes. * Return how a bitmap of a given size would be cropped for a given list of display sizes, if * it was set as wallpaper via {@link #setBitmapWithCrops(Bitmap, Map, boolean, int)} or @@ -1664,7 +1722,8 @@ public class WallpaperManager { * @hide */ @FlaggedApi(FLAG_MULTI_CROP) - @Nullable + @TestApi + @NonNull public List<Rect> getBitmapCrops(@NonNull Point bitmapSize, @NonNull List<Point> displaySizes, @Nullable Map<Point, Rect> cropHints) { try { @@ -1890,9 +1949,14 @@ public class WallpaperManager { * defined kind of wallpaper, either {@link #FLAG_SYSTEM} or {@link #FLAG_LOCK}. * @param getCropped If true the cropped file will be retrieved, if false the original will * be retrieved. - * + * @return A ParcelFileDescriptor for the wallpaper bitmap of the given destination, if it's an + * ImageWallpaper wallpaper. Return {@code null} if the wallpaper is not an + * ImageWallpaper. Also return {@code null} when called with + * which={@link #FLAG_LOCK} if there is a shared home + lock wallpaper. * @hide */ + @FlaggedApi(FLAG_CUSTOMIZATION_PACKS_APIS) + @SystemApi @Nullable public ParcelFileDescriptor getWallpaperFile(@SetWallpaperFlags int which, boolean getCropped) { return getWallpaperFile(which, mContext.getUserId(), getCropped); @@ -2371,7 +2435,6 @@ public class WallpaperManager { /** * Version of setBitmap that defines how the wallpaper will be positioned for different * display sizes. - * Requires permission {@link android.Manifest.permission#SET_WALLPAPER}. * @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 @@ -2380,6 +2443,7 @@ public class WallpaperManager { * @hide */ @FlaggedApi(FLAG_MULTI_CROP) + @TestApi @RequiresPermission(android.Manifest.permission.SET_WALLPAPER) public int setBitmapWithCrops(@Nullable Bitmap fullImage, @NonNull Map<Point, Rect> cropHints, boolean allowBackup, @SetWallpaperFlags int which) throws IOException { @@ -2562,7 +2626,6 @@ public class WallpaperManager { /** * Version of setStream that defines how the wallpaper will be positioned for different * display sizes. - * Requires permission {@link android.Manifest.permission#SET_WALLPAPER}. * @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 @@ -2571,9 +2634,11 @@ public class WallpaperManager { * @hide */ @FlaggedApi(FLAG_MULTI_CROP) + @TestApi @RequiresPermission(android.Manifest.permission.SET_WALLPAPER) - public int setStreamWithCrops(InputStream bitmapData, @NonNull Map<Point, Rect> cropHints, - boolean allowBackup, @SetWallpaperFlags int which) throws IOException { + public int setStreamWithCrops(@NonNull InputStream bitmapData, + @NonNull Map<Point, Rect> cropHints, boolean allowBackup, @SetWallpaperFlags int which) + throws IOException { SparseArray<Rect> crops = new SparseArray<>(); cropHints.forEach((k, v) -> crops.put(getOrientation(k), v)); return setStreamWithCrops(bitmapData, crops, allowBackup, which); @@ -2583,15 +2648,21 @@ public class WallpaperManager { * Similar to {@link #setStreamWithCrops(InputStream, Map, boolean, int)}, but using * {@link ScreenOrientation} as keys of the cropHints map. Used for backup & restore, since * WallpaperBackupAgent stores orientations rather than the exact display size. - * Requires permission {@link android.Manifest.permission#SET_WALLPAPER}. + * @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 cropHints map from {@link ScreenOrientation} to a sub-region of the image to display * for that screen orientation. + * @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_MULTI_CROP) + @SystemApi @RequiresPermission(android.Manifest.permission.SET_WALLPAPER) - public int setStreamWithCrops(InputStream bitmapData, @NonNull SparseArray<Rect> cropHints, - boolean allowBackup, @SetWallpaperFlags int which) throws IOException { + public int setStreamWithCrops(@NonNull InputStream bitmapData, + @NonNull SparseArray<Rect> cropHints, boolean allowBackup, @SetWallpaperFlags int which) + throws IOException { if (sGlobals.mService == null) { Log.w(TAG, "WallpaperService not running"); throw new RuntimeException(new DeadSystemException()); diff --git a/core/java/android/app/ZenBypassingApp.java b/core/java/android/app/ZenBypassingApp.java new file mode 100644 index 000000000000..89bcfa2d2e7d --- /dev/null +++ b/core/java/android/app/ZenBypassingApp.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.os.Parcel; +import android.os.Parcelable; + +import androidx.annotation.NonNull; + +import java.util.Objects; + +/** + * @hide + */ +public final class ZenBypassingApp implements Parcelable { + + @NonNull private String mPkg; + private boolean mAllChannelsBypass; + + + public ZenBypassingApp(@NonNull String pkg, boolean allChannelsBypass) { + mPkg = pkg; + mAllChannelsBypass = allChannelsBypass; + } + + public ZenBypassingApp(Parcel source) { + mPkg = source.readString(); + mAllChannelsBypass = source.readBoolean(); + } + + @NonNull + public String getPkg() { + return mPkg; + } + + public boolean doAllChannelsBypass() { + return mAllChannelsBypass; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeString(mPkg); + dest.writeBoolean(mAllChannelsBypass); + } + + public static final @android.annotation.NonNull Parcelable.Creator<ZenBypassingApp> CREATOR + = new Parcelable.Creator<ZenBypassingApp>() { + @Override + public ZenBypassingApp createFromParcel(Parcel source) { + return new ZenBypassingApp(source); + } + @Override + public ZenBypassingApp[] newArray(int size) { + return new ZenBypassingApp[size]; + } + }; + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof ZenBypassingApp)) return false; + ZenBypassingApp that = (ZenBypassingApp) o; + return mAllChannelsBypass == that.mAllChannelsBypass && Objects.equals(mPkg, + that.mPkg); + } + + @Override + public int hashCode() { + return Objects.hash(mPkg, mAllChannelsBypass); + } + + @Override + public String toString() { + return "ZenBypassingApp{" + + "mPkg='" + mPkg + '\'' + + ", mAllChannelsBypass=" + mAllChannelsBypass + + '}'; + } +} diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 102540c010ae..bff77f951b9f 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -55,8 +55,10 @@ import static android.Manifest.permission.SET_TIME; import static android.Manifest.permission.SET_TIME_ZONE; import static android.app.admin.DeviceAdminInfo.HEADLESS_DEVICE_OWNER_MODE_UNSUPPORTED; import static android.app.admin.flags.Flags.FLAG_DEVICE_THEFT_API_ENABLED; +import static android.app.admin.flags.Flags.FLAG_REMOVE_MANAGED_PROFILE_ENABLED; import static android.app.admin.flags.Flags.onboardingBugreportV2Enabled; import static android.app.admin.flags.Flags.onboardingConsentlessBugreports; +import static android.app.admin.flags.Flags.FLAG_SECONDARY_LOCKSCREEN_API_ENABLED; import static android.content.Intent.LOCAL_FLAG_FROM_SYSTEM; import static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_1; import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE; @@ -8968,12 +8970,9 @@ public class DevicePolicyManager { /** * Called by a device owner, a profile owner for the primary user or a profile * owner of an organization-owned managed profile to turn auto time zone on and off. - * Callers are recommended to use {@link UserManager#DISALLOW_CONFIG_DATE_TIME} - * to prevent the user from changing this setting. * <p> - * If user restriction {@link UserManager#DISALLOW_CONFIG_DATE_TIME} is used, - * no user will be able set the date and time zone. Instead, the network date - * and time zone will be used. + * Callers are recommended to use {@link UserManager#DISALLOW_CONFIG_DATE_TIME} to prevent the + * user from changing this setting, that way no user will be able set the date and time zone. * * @param admin Which {@link DeviceAdminReceiver} this request is associated with or Null if the * caller is not a device admin. @@ -8981,13 +8980,17 @@ public class DevicePolicyManager { * @throws SecurityException if caller is not a device owner, a profile owner for the * primary user, or a profile owner of an organization-owned managed profile. */ - @SupportsCoexistence @RequiresPermission(value = SET_TIME_ZONE, conditional = true) public void setAutoTimeZoneEnabled(@Nullable ComponentName admin, boolean enabled) { throwIfParentInstance("setAutoTimeZone"); if (mService != null) { try { - mService.setAutoTimeZoneEnabled(admin, mContext.getPackageName(), enabled); + if (Flags.setAutoTimeZoneEnabledCoexistence()) { + mService.setAutoTimeZonePolicy(mContext.getPackageName(), + enabled ? AUTO_TIME_ZONE_ENABLED : AUTO_TIME_ZONE_DISABLED ); + } else { + mService.setAutoTimeZoneEnabled(admin, mContext.getPackageName(), enabled); + } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -9017,6 +9020,96 @@ public class DevicePolicyManager { } /** + * Specifies that the auto time zone state is not controlled by device policy. + * + * @see #setAutoTimeZonePolicy(int) + */ + @FlaggedApi(Flags.FLAG_SET_AUTO_TIME_ZONE_ENABLED_COEXISTENCE) + public static final int AUTO_TIME_ZONE_NOT_CONTROLLED_BY_POLICY = 0; + + /** + * Specifies the "disabled" auto time zone state. + * + * @see #setAutoTimeZonePolicy(int) + */ + @FlaggedApi(Flags.FLAG_SET_AUTO_TIME_ZONE_ENABLED_COEXISTENCE) + public static final int AUTO_TIME_ZONE_DISABLED = 1; + + /** + * Specifies the "enabled" auto time zone state. + * + * @see #setAutoTimeZonePolicy(int) + */ + @FlaggedApi(Flags.FLAG_SET_AUTO_TIME_ZONE_ENABLED_COEXISTENCE) + public static final int AUTO_TIME_ZONE_ENABLED = 2; + + /** + * Flags supplied to {@link #setAutoTimeZonePolicy}(int)}. + * + * @hide + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = { "AUTO_TIME_ZONE_" }, value = { + AUTO_TIME_ZONE_NOT_CONTROLLED_BY_POLICY, + AUTO_TIME_ZONE_DISABLED, + AUTO_TIME_ZONE_ENABLED + }) + public @interface AutoTimeZonePolicy {} + + /** + * Called by a device owner, a profile owner for the primary user or a profile owner of an + * organization-owned managed profile to turn auto time zone on and off. + * <p> + * Callers are recommended to use {@link UserManager#DISALLOW_CONFIG_DATE_TIME} to prevent the + * user from changing this setting, that way no user will be able set the date and time zone. + * + * @param policy The desired state among {@link #AUTO_TIME_ZONE_ENABLED} to enable it, + * {@link #AUTO_TIME_ZONE_DISABLED} to disable it or + * {@link #AUTO_TIME_ZONE_NOT_CONTROLLED_BY_POLICY} to unset the policy. + * @throws SecurityException if caller is not a device owner, a profile owner for the primary + * user, or a profile owner of an organization-owned managed profile, or if the caller does not + * hold the required permission. + */ + @SupportsCoexistence + @RequiresPermission(value = SET_TIME_ZONE, conditional = true) + @FlaggedApi(Flags.FLAG_SET_AUTO_TIME_ZONE_ENABLED_COEXISTENCE) + public void setAutoTimeZonePolicy(@AutoTimeZonePolicy int policy) { + throwIfParentInstance("setAutoTimeZonePolicy"); + if (mService != null) { + try { + mService.setAutoTimeZonePolicy(mContext.getPackageName(), policy); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + } + + /** + * Returns auto time zone policy's current state. + * + * @return One of {@link #AUTO_TIME_ZONE_ENABLED} if enabled, {@link #AUTO_TIME_ZONE_DISABLED} + * if disabled and {@link #AUTO_TIME_ZONE_NOT_CONTROLLED_BY_POLICY} if the state is not + * controlled by policy. + * @throws SecurityException if caller is not a device owner, a profile owner for the + * primary user, or a profile owner of an organization-owned managed profile, or if the caller + * does not hold the required permission. + */ + @SupportsCoexistence + @RequiresPermission(anyOf = {SET_TIME_ZONE, QUERY_ADMIN_POLICY}, conditional = true) + @FlaggedApi(Flags.FLAG_SET_AUTO_TIME_ZONE_ENABLED_COEXISTENCE) + public @AutoTimeZonePolicy int getAutoTimeZonePolicy() { + throwIfParentInstance("getAutoTimeZonePolicy"); + if (mService != null) { + try { + return mService.getAutoTimeZonePolicy(mContext.getPackageName()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + return DevicePolicyManager.AUTO_TIME_ZONE_NOT_CONTROLLED_BY_POLICY; + } + + /** * TODO (b/137101239): remove this method in follow-up CL * since it's only used for split system user. * Called by a device owner to set whether all users created on the device should be ephemeral. @@ -12550,28 +12643,43 @@ public class DevicePolicyManager { * @param enabled Whether or not the lockscreen needs to be shown. * @throws SecurityException if {@code admin} is not a device or profile owner. * @see #isSecondaryLockscreenEnabled + * @deprecated Use {@link #setSecondaryLockscreenEnabled(boolean,PersistableBundle)} instead. * @hide - **/ + */ + @Deprecated @SystemApi + @FlaggedApi(FLAG_SECONDARY_LOCKSCREEN_API_ENABLED) public void setSecondaryLockscreenEnabled(@NonNull ComponentName admin, boolean enabled) { - setSecondaryLockscreenEnabled(admin, enabled, null); + throwIfParentInstance("setSecondaryLockscreenEnabled"); + if (mService != null) { + try { + mService.setSecondaryLockscreenEnabled(admin, enabled, null); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } } /** * Called by the system supervision app to set whether a secondary lockscreen needs to be shown. * - * @param admin Which {@link DeviceAdminReceiver} this request is associated with. Null if the - * caller is not a device admin. + * <p>The secondary lockscreen will by displayed after the primary keyguard security screen + * requirements are met. + * + * <p>This API, and associated APIs, can only be called by the default supervision app. + * * @param enabled Whether or not the lockscreen needs to be shown. * @param options A {@link PersistableBundle} to supply options to the lock screen. * @hide */ - public void setSecondaryLockscreenEnabled(@Nullable ComponentName admin, boolean enabled, + @SystemApi + @FlaggedApi(FLAG_SECONDARY_LOCKSCREEN_API_ENABLED) + public void setSecondaryLockscreenEnabled(boolean enabled, @Nullable PersistableBundle options) { throwIfParentInstance("setSecondaryLockscreenEnabled"); if (mService != null) { try { - mService.setSecondaryLockscreenEnabled(admin, enabled, options); + mService.setSecondaryLockscreenEnabled(null, enabled, options); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -16962,6 +17070,30 @@ public class DevicePolicyManager { } /** + * Removes a manged profile from the device only when called from a managed profile's context + * + * @param user UserHandle of the profile to be removed + * @return {@code true} when removal of managed profile was successful, {@code false} when + * removal was unsuccessful or throws IllegalArgumentException when provided user was not a + * managed profile + * @hide + */ + @SystemApi + @UserHandleAware + @FlaggedApi(FLAG_REMOVE_MANAGED_PROFILE_ENABLED) + @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) + public boolean removeManagedProfile() { + if (mService == null) { + throw new IllegalStateException("Could not find DevicePolicyManagerService"); + } + try { + return mService.removeManagedProfile(myUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Called when a managed profile has been provisioned. * * @throws SecurityException if the caller does not hold diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index a4e2b8f62a23..0b8f53881d07 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -378,6 +378,9 @@ interface IDevicePolicyManager { void setAutoTimeZoneEnabled(in ComponentName who, String callerPackageName, boolean enabled); boolean getAutoTimeZoneEnabled(in ComponentName who, String callerPackageName); + void setAutoTimeZonePolicy(String callerPackageName, int policy); + int getAutoTimeZonePolicy(String callerPackageName); + void setForceEphemeralUsers(in ComponentName who, boolean forceEpehemeralUsers); boolean getForceEphemeralUsers(in ComponentName who); @@ -567,6 +570,8 @@ interface IDevicePolicyManager { void finalizeWorkProfileProvisioning(in UserHandle managedProfileUser, in Account migratedAccount); + boolean removeManagedProfile(int userId); + void setDeviceOwnerType(in ComponentName admin, in int deviceOwnerType); int getDeviceOwnerType(in ComponentName admin); diff --git a/core/java/android/app/admin/flags/flags.aconfig b/core/java/android/app/admin/flags/flags.aconfig index fee071b14016..404471e266d2 100644 --- a/core/java/android/app/admin/flags/flags.aconfig +++ b/core/java/android/app/admin/flags/flags.aconfig @@ -343,13 +343,20 @@ flag { } flag { - name: "user_provisioning_same_state" - namespace: "enterprise" - description: "Handle exceptions while setting same provisioning state." - bug: "326441417" - metadata { - purpose: PURPOSE_BUGFIX - } + name: "user_provisioning_same_state" + namespace: "enterprise" + description: "Handle exceptions while setting same provisioning state." + bug: "326441417" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { + name: "remove_managed_profile_enabled" + namespace: "enterprise" + description: "API that removes a given managed profile." + bug: "372652841" } flag { @@ -367,3 +374,18 @@ flag { description: "Allows DPMS to enable or disable SupervisionService based on whether the device is being managed by the supervision role holder." bug: "376213673" } + +flag { + name: "split_create_managed_profile_enabled" + namespace: "enterprise" + description: "Split up existing create and provision managed profile API." + bug: "375382324" +} + +flag { + name: "secondary_lockscreen_api_enabled" + is_exported: true + namespace: "enterprise" + description: "Add new API for secondary lockscreen" + bug: "336297680" +} diff --git a/core/java/android/app/appfunctions/AppFunctionException.aidl b/core/java/android/app/appfunctions/AppFunctionException.aidl new file mode 100644 index 000000000000..7d432243b1b2 --- /dev/null +++ b/core/java/android/app/appfunctions/AppFunctionException.aidl @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.appfunctions; + +import android.app.appfunctions.AppFunctionException; + +parcelable AppFunctionException;
\ No newline at end of file diff --git a/core/java/android/app/appfunctions/AppFunctionException.java b/core/java/android/app/appfunctions/AppFunctionException.java new file mode 100644 index 000000000000..cbd1d932ab00 --- /dev/null +++ b/core/java/android/app/appfunctions/AppFunctionException.java @@ -0,0 +1,261 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.app.appfunctions; + +import static android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER; + +import android.annotation.FlaggedApi; +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Objects; + +/** Represents an app function related errors. */ +@FlaggedApi(FLAG_ENABLE_APP_FUNCTION_MANAGER) +public final class AppFunctionException extends Exception implements Parcelable { + /** + * The caller does not have the permission to execute an app function. + * + * <p>This error is in the {@link #ERROR_CATEGORY_REQUEST_ERROR} category. + */ + public static final int ERROR_DENIED = 1000; + + /** + * The caller supplied invalid arguments to the execution request. + * + * <p>This error may be considered similar to {@link IllegalArgumentException}. + * + * <p>This error is in the {@link #ERROR_CATEGORY_REQUEST_ERROR} category. + */ + public static final int ERROR_INVALID_ARGUMENT = 1001; + + /** + * The caller tried to execute a disabled app function. + * + * <p>This error is in the {@link #ERROR_CATEGORY_REQUEST_ERROR} category. + */ + public static final int ERROR_DISABLED = 1002; + + /** + * The caller tried to execute a function that does not exist. + * + * <p>This error is in the {@link #ERROR_CATEGORY_REQUEST_ERROR} category. + */ + public static final int ERROR_FUNCTION_NOT_FOUND = 1003; + + /** + * An internal unexpected error coming from the system. + * + * <p>This error is in the {@link #ERROR_CATEGORY_SYSTEM} category. + */ + public static final int ERROR_SYSTEM_ERROR = 2000; + + /** + * The operation was cancelled. Use this error code to report that a cancellation is done after + * receiving a cancellation signal. + * + * <p>This error is in the {@link #ERROR_CATEGORY_SYSTEM} category. + */ + public static final int ERROR_CANCELLED = 2001; + + /** + * An unknown error occurred while processing the call in the AppFunctionService. + * + * <p>This error is thrown when the service is connected in the remote application but an + * unexpected error is thrown from the bound application. + * + * <p>This error is in the {@link #ERROR_CATEGORY_APP} category. + */ + public static final int ERROR_APP_UNKNOWN_ERROR = 3000; + + /** + * The error category is unknown. + * + * <p>This is the default value for {@link #getErrorCategory}. + */ + public static final int ERROR_CATEGORY_UNKNOWN = 0; + + /** + * The error is caused by the app requesting a function execution. + * + * <p>For example, the caller provided invalid parameters in the execution request e.g. an + * invalid function ID. + * + * <p>Errors in the category fall in the range 1000-1999 inclusive. + */ + public static final int ERROR_CATEGORY_REQUEST_ERROR = 1; + + /** + * The error is caused by an issue in the system. + * + * <p>For example, the AppFunctionService implementation is not found by the system. + * + * <p>Errors in the category fall in the range 2000-2999 inclusive. + */ + public static final int ERROR_CATEGORY_SYSTEM = 2; + + /** + * The error is caused by the app providing the function. + * + * <p>For example, the app crashed when the system is executing the request. + * + * <p>Errors in the category fall in the range 3000-3999 inclusive. + */ + public static final int ERROR_CATEGORY_APP = 3; + + private final int mErrorCode; + @Nullable private final String mErrorMessage; + @NonNull private final Bundle mExtras; + + /** + * @param errorCode The error code. + * @param errorMessage The error message. + */ + public AppFunctionException(@ErrorCode int errorCode, @Nullable String errorMessage) { + this(errorCode, errorMessage, Bundle.EMPTY); + } + + /** + * @param errorCode The error code. + * @param errorMessage The error message. + * @param extras The extras associated with this error. + */ + public AppFunctionException( + @ErrorCode int errorCode, @Nullable String errorMessage, @NonNull Bundle extras) { + super(errorMessage); + mErrorCode = errorCode; + mErrorMessage = errorMessage; + mExtras = Objects.requireNonNull(extras); + } + + private AppFunctionException(@NonNull Parcel in) { + mErrorCode = in.readInt(); + mErrorMessage = in.readString8(); + mExtras = Objects.requireNonNull(in.readBundle(getClass().getClassLoader())); + } + + /** Returns one of the {@code ERROR} constants. */ + @ErrorCode + public int getErrorCode() { + return mErrorCode; + } + + /** Returns the error message. */ + @Nullable + public String getErrorMessage() { + return mErrorMessage; + } + + /** + * Returns the error category. + * + * <p>This method categorizes errors based on their underlying cause, allowing developers to + * implement targeted error handling and provide more informative error messages to users. It + * maps ranges of error codes to specific error categories. + * + * <p>This method returns {@code ERROR_CATEGORY_UNKNOWN} if the error code does not belong to + * any error category. + * + * <p>See {@link ErrorCategory} for a complete list of error categories and their corresponding + * error code ranges. + */ + @ErrorCategory + public int getErrorCategory() { + if (mErrorCode >= 1000 && mErrorCode < 2000) { + return ERROR_CATEGORY_REQUEST_ERROR; + } + if (mErrorCode >= 2000 && mErrorCode < 3000) { + return ERROR_CATEGORY_SYSTEM; + } + if (mErrorCode >= 3000 && mErrorCode < 4000) { + return ERROR_CATEGORY_APP; + } + return ERROR_CATEGORY_UNKNOWN; + } + + /** Returns any extras associated with this error. */ + @NonNull + public Bundle getExtras() { + return mExtras; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mErrorCode); + dest.writeString8(mErrorMessage); + dest.writeBundle(mExtras); + } + + /** + * Error codes. + * + * @hide + */ + @IntDef( + prefix = {"ERROR_"}, + value = { + ERROR_DENIED, + ERROR_APP_UNKNOWN_ERROR, + ERROR_FUNCTION_NOT_FOUND, + ERROR_SYSTEM_ERROR, + ERROR_INVALID_ARGUMENT, + ERROR_DISABLED, + ERROR_CANCELLED + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ErrorCode {} + + /** + * Error categories. + * + * @hide + */ + @IntDef( + prefix = {"ERROR_CATEGORY_"}, + value = { + ERROR_CATEGORY_UNKNOWN, + ERROR_CATEGORY_REQUEST_ERROR, + ERROR_CATEGORY_APP, + ERROR_CATEGORY_SYSTEM + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ErrorCategory {} + + @NonNull + public static final Creator<AppFunctionException> CREATOR = + new Creator<>() { + @Override + public AppFunctionException createFromParcel(Parcel in) { + return new AppFunctionException(in); + } + + @Override + public AppFunctionException[] newArray(int size) { + return new AppFunctionException[size]; + } + }; +} diff --git a/core/java/android/app/appfunctions/AppFunctionManager.java b/core/java/android/app/appfunctions/AppFunctionManager.java index 5ddb590add4c..ed088fed41c2 100644 --- a/core/java/android/app/appfunctions/AppFunctionManager.java +++ b/core/java/android/app/appfunctions/AppFunctionManager.java @@ -16,7 +16,7 @@ package android.app.appfunctions; -import static android.app.appfunctions.ExecuteAppFunctionResponse.getResultCode; +import static android.app.appfunctions.AppFunctionException.ERROR_SYSTEM_ERROR; import static android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER; import android.Manifest; @@ -39,7 +39,6 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Objects; import java.util.concurrent.Executor; -import java.util.function.Consumer; /** * Provides access to app functions. @@ -147,16 +146,16 @@ public final class AppFunctionManager { * @param request the request to execute the app function * @param executor the executor to run the callback * @param cancellationSignal the cancellation signal to cancel the execution. - * @param callback the callback to receive the function execution result. + * @param callback the callback to receive the function execution result or error. * <p>If the calling app does not own the app function or does not have {@code * android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED} or {@code * android.permission.EXECUTE_APP_FUNCTIONS}, the execution result will contain {@code - * ExecuteAppFunctionResponse.RESULT_DENIED}. + * AppFunctionException.ERROR_DENIED}. * <p>If the caller only has {@code android.permission.EXECUTE_APP_FUNCTIONS} but the * function requires {@code android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED}, the execution - * result will contain {@code ExecuteAppFunctionResponse.RESULT_DENIED} + * result will contain {@code AppFunctionException.ERROR_DENIED} * <p>If the function requested for execution is disabled, then the execution result will - * contain {@code ExecuteAppFunctionResponse.RESULT_DISABLED} + * contain {@code AppFunctionException.ERROR_DISABLED} * <p>If the cancellation signal is issued, the operation is cancelled and no response is * returned to the caller. */ @@ -171,7 +170,9 @@ public final class AppFunctionManager { @NonNull ExecuteAppFunctionRequest request, @NonNull @CallbackExecutor Executor executor, @NonNull CancellationSignal cancellationSignal, - @NonNull Consumer<ExecuteAppFunctionResponse> callback) { + @NonNull + OutcomeReceiver<ExecuteAppFunctionResponse, AppFunctionException> + callback) { Objects.requireNonNull(request); Objects.requireNonNull(executor); Objects.requireNonNull(callback); @@ -186,20 +187,25 @@ public final class AppFunctionManager { aidlRequest, new IExecuteAppFunctionCallback.Stub() { @Override - public void onResult(ExecuteAppFunctionResponse result) { + public void onSuccess(ExecuteAppFunctionResponse result) { try { - executor.execute(() -> callback.accept(result)); + executor.execute(() -> callback.onResult(result)); } catch (RuntimeException e) { // Ideally shouldn't happen since errors are wrapped into - // the - // response, but we catch it here for additional safety. - callback.accept( - ExecuteAppFunctionResponse.newFailure( - getResultCode(e), - e.getMessage(), - /* extras= */ null)); + // the response, but we catch it here for additional safety. + executor.execute( + () -> + callback.onError( + new AppFunctionException( + ERROR_SYSTEM_ERROR, + e.getMessage()))); } } + + @Override + public void onError(AppFunctionException exception) { + executor.execute(() -> callback.onError(exception)); + } }); if (cancellationTransport != null) { cancellationSignal.setRemote(cancellationTransport); diff --git a/core/java/android/app/appfunctions/AppFunctionRuntimeMetadata.java b/core/java/android/app/appfunctions/AppFunctionRuntimeMetadata.java index 06d95f5270c3..3ddda228d145 100644 --- a/core/java/android/app/appfunctions/AppFunctionRuntimeMetadata.java +++ b/core/java/android/app/appfunctions/AppFunctionRuntimeMetadata.java @@ -213,9 +213,7 @@ public class AppFunctionRuntimeMetadata extends GenericDocument { setEnabled(original.getEnabled()); } - /** - * Sets an indicator specifying the function enabled state. - */ + /** Sets an indicator specifying the function enabled state. */ @NonNull public Builder setEnabled(@EnabledState int enabledState) { if (enabledState != APP_FUNCTION_STATE_DEFAULT diff --git a/core/java/android/app/appfunctions/AppFunctionService.java b/core/java/android/app/appfunctions/AppFunctionService.java index 63d187aa11ef..85b6ab2b4e61 100644 --- a/core/java/android/app/appfunctions/AppFunctionService.java +++ b/core/java/android/app/appfunctions/AppFunctionService.java @@ -17,7 +17,6 @@ package android.app.appfunctions; import static android.Manifest.permission.BIND_APP_FUNCTION_SERVICE; -import static android.app.appfunctions.ExecuteAppFunctionResponse.getResultCode; import static android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER; import static android.content.pm.PackageManager.PERMISSION_DENIED; @@ -32,10 +31,8 @@ import android.os.Binder; import android.os.CancellationSignal; import android.os.IBinder; import android.os.ICancellationSignal; +import android.os.OutcomeReceiver; import android.os.RemoteException; -import android.util.Log; - -import java.util.function.Consumer; /** * Abstract base class to provide app functions to the system. @@ -80,7 +77,9 @@ public abstract class AppFunctionService extends Service { @NonNull ExecuteAppFunctionRequest request, @NonNull String callingPackage, @NonNull CancellationSignal cancellationSignal, - @NonNull Consumer<ExecuteAppFunctionResponse> callback); + @NonNull + OutcomeReceiver<ExecuteAppFunctionResponse, AppFunctionException> + callback); } /** @hide */ @@ -105,13 +104,22 @@ public abstract class AppFunctionService extends Service { request, callingPackage, buildCancellationSignal(cancellationCallback), - safeCallback::onResult); + new OutcomeReceiver<>() { + @Override + public void onResult(ExecuteAppFunctionResponse result) { + safeCallback.onResult(result); + } + + @Override + public void onError(AppFunctionException exception) { + safeCallback.onError(exception); + } + }); } catch (Exception ex) { // Apps should handle exceptions. But if they don't, report the error on // behalf of them. - safeCallback.onResult( - ExecuteAppFunctionResponse.newFailure( - getResultCode(ex), ex.getMessage(), /* extras= */ null)); + safeCallback.onError( + new AppFunctionException(toErrorCode(ex), ex.getMessage())); } } }; @@ -164,12 +172,26 @@ public abstract class AppFunctionService extends Service { * @param request The function execution request. * @param callingPackage The package name of the app that is requesting the execution. * @param cancellationSignal A signal to cancel the execution. - * @param callback A callback to report back the result. + * @param callback A callback to report back the result or error. */ @MainThread public abstract void onExecuteFunction( @NonNull ExecuteAppFunctionRequest request, @NonNull String callingPackage, @NonNull CancellationSignal cancellationSignal, - @NonNull Consumer<ExecuteAppFunctionResponse> callback); + @NonNull + OutcomeReceiver<ExecuteAppFunctionResponse, AppFunctionException> + callback); + + /** + * Returns result codes from throwable. + * + * @hide + */ + private static @AppFunctionException.ErrorCode int toErrorCode(@NonNull Throwable t) { + if (t instanceof IllegalArgumentException) { + return AppFunctionException.ERROR_INVALID_ARGUMENT; + } + return AppFunctionException.ERROR_APP_UNKNOWN_ERROR; + } } diff --git a/core/java/android/app/appfunctions/AppFunctionStaticMetadataHelper.java b/core/java/android/app/appfunctions/AppFunctionStaticMetadataHelper.java index a23f842e6eeb..1869d22ea080 100644 --- a/core/java/android/app/appfunctions/AppFunctionStaticMetadataHelper.java +++ b/core/java/android/app/appfunctions/AppFunctionStaticMetadataHelper.java @@ -38,7 +38,7 @@ public class AppFunctionStaticMetadataHelper { public static final String STATIC_SCHEMA_TYPE = "AppFunctionStaticMetadata"; public static final String STATIC_PROPERTY_ENABLED_BY_DEFAULT = "enabledByDefault"; public static final String STATIC_PROPERTY_RESTRICT_CALLERS_WITH_EXECUTE_APP_FUNCTIONS = - "restrictCallersWithExecuteAppFunctions"; + "restrictCallersWithExecuteAppFunctions"; public static final String APP_FUNCTION_STATIC_NAMESPACE = "app_functions"; public static final String PROPERTY_FUNCTION_ID = "functionId"; diff --git a/core/java/android/app/appfunctions/ExecuteAppFunctionRequest.java b/core/java/android/app/appfunctions/ExecuteAppFunctionRequest.java index 41bb62270e9f..1557815a8468 100644 --- a/core/java/android/app/appfunctions/ExecuteAppFunctionRequest.java +++ b/core/java/android/app/appfunctions/ExecuteAppFunctionRequest.java @@ -111,8 +111,8 @@ public final class ExecuteAppFunctionRequest implements Parcelable { * Returns the function parameters. The key is the parameter name, and the value is the * parameter value. * - * <p>The bundle may have missing parameters. Developers are advised to implement defensive - * handling measures. + * <p>The {@link GenericDocument} may have missing parameters. Developers are advised to + * implement defensive handling measures. * * @see AppFunctionManager on how to determine the expected parameters. */ diff --git a/core/java/android/app/appfunctions/ExecuteAppFunctionResponse.java b/core/java/android/app/appfunctions/ExecuteAppFunctionResponse.java index cdf02e6f5a09..acad43b782e5 100644 --- a/core/java/android/app/appfunctions/ExecuteAppFunctionResponse.java +++ b/core/java/android/app/appfunctions/ExecuteAppFunctionResponse.java @@ -19,16 +19,12 @@ package android.app.appfunctions; import static android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER; import android.annotation.FlaggedApi; -import android.annotation.IntDef; import android.annotation.NonNull; -import android.annotation.Nullable; import android.app.appsearch.GenericDocument; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; import java.util.Objects; /** The response to an app function execution. */ @@ -45,10 +41,7 @@ public final class ExecuteAppFunctionResponse implements Parcelable { Bundle extras = Objects.requireNonNull( parcel.readBundle(Bundle.class.getClassLoader())); - int resultCode = parcel.readInt(); - String errorMessage = parcel.readString8(); - return new ExecuteAppFunctionResponse( - resultWrapper, extras, resultCode, errorMessage); + return new ExecuteAppFunctionResponse(resultWrapper.getValue(), extras); } @Override @@ -71,113 +64,7 @@ public final class ExecuteAppFunctionResponse implements Parcelable { * * <p>See {@link #getResultDocument} for more information on extracting the return value. */ - public static final String PROPERTY_RETURN_VALUE = "returnValue"; - - /** - * The call was successful. - * - * <p>This result code does not belong in an error category. - */ - public static final int RESULT_OK = 0; - - /** - * The caller does not have the permission to execute an app function. - * - * <p>This error is in the {@link #ERROR_CATEGORY_REQUEST_ERROR} category. - */ - public static final int RESULT_DENIED = 1000; - - /** - * The caller supplied invalid arguments to the execution request. - * - * <p>This error may be considered similar to {@link IllegalArgumentException}. - * - * <p>This error is in the {@link #ERROR_CATEGORY_REQUEST_ERROR} category. - */ - public static final int RESULT_INVALID_ARGUMENT = 1001; - - /** - * The caller tried to execute a disabled app function. - * - * <p>This error is in the {@link #ERROR_CATEGORY_REQUEST_ERROR} category. - */ - public static final int RESULT_DISABLED = 1002; - - /** - * The caller tried to execute a function that does not exist. - * - * <p>This error is in the {@link #ERROR_CATEGORY_REQUEST_ERROR} category. - */ - public static final int RESULT_FUNCTION_NOT_FOUND = 1003; - - /** - * An internal unexpected error coming from the system. - * - * <p>This error is in the {@link #ERROR_CATEGORY_SYSTEM} category. - */ - public static final int RESULT_SYSTEM_ERROR = 2000; - - /** - * The operation was cancelled. Use this error code to report that a cancellation is done after - * receiving a cancellation signal. - * - * <p>This error is in the {@link #ERROR_CATEGORY_SYSTEM} category. - */ - public static final int RESULT_CANCELLED = 2001; - - /** - * An unknown error occurred while processing the call in the AppFunctionService. - * - * <p>This error is thrown when the service is connected in the remote application but an - * unexpected error is thrown from the bound application. - * - * <p>This error is in the {@link #ERROR_CATEGORY_APP} category. - */ - public static final int RESULT_APP_UNKNOWN_ERROR = 3000; - - /** - * The error category is unknown. - * - * <p>This is the default value for {@link #getErrorCategory}. - */ - public static final int ERROR_CATEGORY_UNKNOWN = 0; - - /** - * The error is caused by the app requesting a function execution. - * - * <p>For example, the caller provided invalid parameters in the execution request e.g. an - * invalid function ID. - * - * <p>Errors in the category fall in the range 1000-1999 inclusive. - */ - public static final int ERROR_CATEGORY_REQUEST_ERROR = 1; - - /** - * The error is caused by an issue in the system. - * - * <p>For example, the AppFunctionService implementation is not found by the system. - * - * <p>Errors in the category fall in the range 2000-2999 inclusive. - */ - public static final int ERROR_CATEGORY_SYSTEM = 2; - - /** - * The error is caused by the app providing the function. - * - * <p>For example, the app crashed when the system is executing the request. - * - * <p>Errors in the category fall in the range 3000-3999 inclusive. - */ - public static final int ERROR_CATEGORY_APP = 3; - - /** The result code of the app function execution. */ - @ResultCode private final int mResultCode; - - /** - * The error message associated with the result, if any. This is {@code null} if the result code - * is {@link #RESULT_OK}. - */ - @Nullable private final String mErrorMessage; + public static final String PROPERTY_RETURN_VALUE = "androidAppfunctionsReturnValue"; /** * Returns the return value of the executed function. @@ -192,103 +79,21 @@ public final class ExecuteAppFunctionResponse implements Parcelable { /** Returns the additional metadata data relevant to this function execution response. */ @NonNull private final Bundle mExtras; - private ExecuteAppFunctionResponse( - @NonNull GenericDocumentWrapper resultDocumentWrapper, - @NonNull Bundle extras, - @ResultCode int resultCode, - @Nullable String errorMessage) { - mResultDocumentWrapper = Objects.requireNonNull(resultDocumentWrapper); - mExtras = Objects.requireNonNull(extras); - mResultCode = resultCode; - mErrorMessage = errorMessage; - } - /** - * Returns result codes from throwable. - * - * @hide - */ - @FlaggedApi(FLAG_ENABLE_APP_FUNCTION_MANAGER) - static @ResultCode int getResultCode(@NonNull Throwable t) { - if (t instanceof IllegalArgumentException) { - return ExecuteAppFunctionResponse.RESULT_INVALID_ARGUMENT; - } - return ExecuteAppFunctionResponse.RESULT_APP_UNKNOWN_ERROR; - } - - /** - * Returns a successful response. - * * @param resultDocument The return value of the executed function. - * @param extras The additional metadata for this function execution response. */ - @NonNull - @FlaggedApi(FLAG_ENABLE_APP_FUNCTION_MANAGER) - public static ExecuteAppFunctionResponse newSuccess( - @NonNull GenericDocument resultDocument, @Nullable Bundle extras) { - Objects.requireNonNull(resultDocument); - Bundle actualExtras = getActualExtras(extras); - GenericDocumentWrapper resultDocumentWrapper = new GenericDocumentWrapper(resultDocument); - - return new ExecuteAppFunctionResponse( - resultDocumentWrapper, actualExtras, RESULT_OK, /* errorMessage= */ null); + public ExecuteAppFunctionResponse(@NonNull GenericDocument resultDocument) { + this(resultDocument, Bundle.EMPTY); } /** - * Returns a failure response. - * - * @param resultCode The result code of the app function execution. + * @param resultDocument The return value of the executed function. * @param extras The additional metadata for this function execution response. - * @param errorMessage The error message associated with the result, if any. - */ - @NonNull - @FlaggedApi(FLAG_ENABLE_APP_FUNCTION_MANAGER) - public static ExecuteAppFunctionResponse newFailure( - @ResultCode int resultCode, @Nullable String errorMessage, @Nullable Bundle extras) { - if (resultCode == RESULT_OK) { - throw new IllegalArgumentException("resultCode must not be RESULT_OK"); - } - Bundle actualExtras = getActualExtras(extras); - GenericDocumentWrapper emptyWrapper = - new GenericDocumentWrapper(new GenericDocument.Builder<>("", "", "").build()); - return new ExecuteAppFunctionResponse(emptyWrapper, actualExtras, resultCode, errorMessage); - } - - private static Bundle getActualExtras(@Nullable Bundle extras) { - if (extras == null) { - return Bundle.EMPTY; - } - return extras; - } - - /** - * Returns the error category of the {@link ExecuteAppFunctionResponse}. - * - * <p>This method categorizes errors based on their underlying cause, allowing developers to - * implement targeted error handling and provide more informative error messages to users. It - * maps ranges of result codes to specific error categories. - * - * <p>When constructing a {@link #newFailure} response, use the appropriate result code value to - * ensure correct categorization of the failed response. - * - * <p>This method returns {@code ERROR_CATEGORY_UNKNOWN} if the result code does not belong to - * any error category, for example, in the case of a successful result with {@link #RESULT_OK}. - * - * <p>See {@link ErrorCategory} for a complete list of error categories and their corresponding - * result code ranges. */ - @ErrorCategory - public int getErrorCategory() { - if (mResultCode >= 1000 && mResultCode < 2000) { - return ERROR_CATEGORY_REQUEST_ERROR; - } - if (mResultCode >= 2000 && mResultCode < 3000) { - return ERROR_CATEGORY_SYSTEM; - } - if (mResultCode >= 3000 && mResultCode < 4000) { - return ERROR_CATEGORY_APP; - } - return ERROR_CATEGORY_UNKNOWN; + public ExecuteAppFunctionResponse( + @NonNull GenericDocument resultDocument, @NonNull Bundle extras) { + mResultDocumentWrapper = new GenericDocumentWrapper(Objects.requireNonNull(resultDocument)); + mExtras = Objects.requireNonNull(extras); } /** @@ -296,9 +101,6 @@ public final class ExecuteAppFunctionResponse implements Parcelable { * * <p>The {@link #PROPERTY_RETURN_VALUE} key can be used to obtain the return value. * - * <p>An empty document is returned if {@link #isSuccess} is {@code false} or if the executed - * function does not produce a return value. - * * <p>Sample code for extracting the return value: * * <pre> @@ -324,32 +126,6 @@ public final class ExecuteAppFunctionResponse implements Parcelable { return mExtras; } - /** - * Returns {@code true} if {@link #getResultCode} equals {@link - * ExecuteAppFunctionResponse#RESULT_OK}. - */ - public boolean isSuccess() { - return getResultCode() == RESULT_OK; - } - - /** - * Returns one of the {@code RESULT} constants defined in {@link ExecuteAppFunctionResponse}. - */ - @ResultCode - public int getResultCode() { - return mResultCode; - } - - /** - * Returns the error message associated with this result. - * - * <p>If {@link #isSuccess} is {@code true}, the error message is always {@code null}. - */ - @Nullable - public String getErrorMessage() { - return mErrorMessage; - } - @Override public int describeContents() { return 0; @@ -359,43 +135,5 @@ public final class ExecuteAppFunctionResponse implements Parcelable { public void writeToParcel(@NonNull Parcel dest, int flags) { mResultDocumentWrapper.writeToParcel(dest, flags); dest.writeBundle(mExtras); - dest.writeInt(mResultCode); - dest.writeString8(mErrorMessage); } - - /** - * Result codes. - * - * @hide - */ - @IntDef( - prefix = {"RESULT_"}, - value = { - RESULT_OK, - RESULT_DENIED, - RESULT_APP_UNKNOWN_ERROR, - RESULT_FUNCTION_NOT_FOUND, - RESULT_SYSTEM_ERROR, - RESULT_INVALID_ARGUMENT, - RESULT_DISABLED, - RESULT_CANCELLED - }) - @Retention(RetentionPolicy.SOURCE) - public @interface ResultCode {} - - /** - * Error categories. - * - * @hide - */ - @IntDef( - prefix = {"ERROR_CATEGORY_"}, - value = { - ERROR_CATEGORY_UNKNOWN, - ERROR_CATEGORY_REQUEST_ERROR, - ERROR_CATEGORY_APP, - ERROR_CATEGORY_SYSTEM - }) - @Retention(RetentionPolicy.SOURCE) - public @interface ErrorCategory {} } diff --git a/core/java/android/app/appfunctions/GenericDocumentWrapper.java b/core/java/android/app/appfunctions/GenericDocumentWrapper.java index b29b64e44d21..541ca7458efe 100644 --- a/core/java/android/app/appfunctions/GenericDocumentWrapper.java +++ b/core/java/android/app/appfunctions/GenericDocumentWrapper.java @@ -34,9 +34,9 @@ import java.util.Objects; * <p>{#link {@link Parcel#writeBlob(byte[])}} could take care of whether to pass data via binder * directly or Android shared memory if the data is large. * - * <p>This class performs lazy unparcelling. The `GenericDocument` is only unparcelled - * from the underlying `Parcel` when {@link #getValue()} is called. This optimization - * allows the system server to pass through the generic document, without unparcel and parcel it. + * <p>This class performs lazy unparcelling. The `GenericDocument` is only unparcelled from the + * underlying `Parcel` when {@link #getValue()} is called. This optimization allows the system + * server to pass through the generic document, without unparcel and parcel it. * * @hide * @see Parcel#writeBlob(byte[]) @@ -45,8 +45,11 @@ public final class GenericDocumentWrapper implements Parcelable { @Nullable @GuardedBy("mLock") private GenericDocument mGenericDocument; + @GuardedBy("mLock") - @Nullable private Parcel mParcel; + @Nullable + private Parcel mParcel; + private final Object mLock = new Object(); public static final Creator<GenericDocumentWrapper> CREATOR = diff --git a/core/java/android/app/appfunctions/IExecuteAppFunctionCallback.aidl b/core/java/android/app/appfunctions/IExecuteAppFunctionCallback.aidl index 5323f9b627e3..69bbc0e5d275 100644 --- a/core/java/android/app/appfunctions/IExecuteAppFunctionCallback.aidl +++ b/core/java/android/app/appfunctions/IExecuteAppFunctionCallback.aidl @@ -17,8 +17,10 @@ package android.app.appfunctions; import android.app.appfunctions.ExecuteAppFunctionResponse; +import android.app.appfunctions.AppFunctionException; /** {@hide} */ oneway interface IExecuteAppFunctionCallback { - void onResult(in ExecuteAppFunctionResponse result); + void onSuccess(in ExecuteAppFunctionResponse result); + void onError(in AppFunctionException exception); } diff --git a/core/java/android/app/appfunctions/SafeOneTimeExecuteAppFunctionCallback.java b/core/java/android/app/appfunctions/SafeOneTimeExecuteAppFunctionCallback.java index 00182447e9a8..2426daf5c9f2 100644 --- a/core/java/android/app/appfunctions/SafeOneTimeExecuteAppFunctionCallback.java +++ b/core/java/android/app/appfunctions/SafeOneTimeExecuteAppFunctionCallback.java @@ -17,17 +17,16 @@ package android.app.appfunctions; import android.annotation.NonNull; -import android.annotation.Nullable; import android.os.RemoteException; import android.util.Log; import java.util.Objects; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.function.Consumer; /** * A wrapper of IExecuteAppFunctionCallback which swallows the {@link RemoteException}. This - * callback is intended for one-time use only. Subsequent calls to onResult() will be ignored. + * callback is intended for one-time use only. Subsequent calls to onResult() or onError() will be + * ignored. * * @hide */ @@ -38,44 +37,41 @@ public class SafeOneTimeExecuteAppFunctionCallback { @NonNull private final IExecuteAppFunctionCallback mCallback; - @Nullable private final Consumer<ExecuteAppFunctionResponse> mOnDispatchCallback; - public SafeOneTimeExecuteAppFunctionCallback(@NonNull IExecuteAppFunctionCallback callback) { - this(callback, /* onDispatchCallback= */ null); - } - - /** - * @param callback The callback to wrap. - * @param onDispatchCallback An optional callback invoked after the wrapped callback has been - * dispatched with a result. This callback receives the result that has been dispatched. - */ - public SafeOneTimeExecuteAppFunctionCallback( - @NonNull IExecuteAppFunctionCallback callback, - @Nullable Consumer<ExecuteAppFunctionResponse> onDispatchCallback) { mCallback = Objects.requireNonNull(callback); - mOnDispatchCallback = onDispatchCallback; } /** Invoke wrapped callback with the result. */ public void onResult(@NonNull ExecuteAppFunctionResponse result) { if (!mOnResultCalled.compareAndSet(false, true)) { - Log.w(TAG, "Ignore subsequent calls to onResult()"); + Log.w(TAG, "Ignore subsequent calls to onResult/onError()"); return; } try { - mCallback.onResult(result); + mCallback.onSuccess(result); } catch (RemoteException ex) { // Failed to notify the other end. Ignore. Log.w(TAG, "Failed to invoke the callback", ex); } - if (mOnDispatchCallback != null) { - mOnDispatchCallback.accept(result); + } + + /** Invoke wrapped callback with the error. */ + public void onError(@NonNull AppFunctionException error) { + if (!mOnResultCalled.compareAndSet(false, true)) { + Log.w(TAG, "Ignore subsequent calls to onResult/onError()"); + return; + } + try { + mCallback.onError(error); + } catch (RemoteException ex) { + // Failed to notify the other end. Ignore. + Log.w(TAG, "Failed to invoke the callback", ex); } } /** - * Disables this callback. Subsequent calls to {@link #onResult(ExecuteAppFunctionResponse)} - * will be ignored. + * Disables this callback. Subsequent calls to {@link #onResult(ExecuteAppFunctionResponse)} or + * {@link #onError(AppFunctionException)} will be ignored. */ public void disable() { mOnResultCalled.set(true); diff --git a/core/java/android/app/assist/AssistContent.java b/core/java/android/app/assist/AssistContent.java index a48868906487..43a46ba7885d 100644 --- a/core/java/android/app/assist/AssistContent.java +++ b/core/java/android/app/assist/AssistContent.java @@ -1,5 +1,6 @@ package android.app.assist; +import android.annotation.FlaggedApi; import android.compat.annotation.UnsupportedAppUsage; import android.content.ClipData; import android.content.Intent; @@ -15,6 +16,20 @@ import android.os.Parcelable; * {@link android.app.Activity#onProvideAssistContent Activity.onProvideAssistContent}. */ public class AssistContent implements Parcelable { + /** + * Extra for a {@link Bundle} that provides contextual AppFunction's information about the + * content currently being viewed in the application. + * <p> + * This extra can be optionally supplied in the {@link AssistContent#getExtras()} bundle. + * <p> + * The schema of the {@link Bundle} in this extra is defined in the AppFunction SDK. + * + * @see android.app.appfunctions.AppFunctionManager + */ + @FlaggedApi(android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER) + public static final String EXTRA_APP_FUNCTION_DATA = + "android.app.assist.extra.APP_FUNCTION_DATA"; + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) private boolean mIsAppProvidedIntent = false; private boolean mIsAppProvidedWebUri = false; diff --git a/core/java/android/app/jank/AppJankStats.java b/core/java/android/app/jank/AppJankStats.java new file mode 100644 index 000000000000..eea1d2ba5b9e --- /dev/null +++ b/core/java/android/app/jank/AppJankStats.java @@ -0,0 +1,253 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.jank; + +import android.annotation.FlaggedApi; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.StringDef; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * This class stores detailed jank statistics for an individual UI widget. These statistics + * provide performance insights for specific UI widget states by correlating the number of + * "Janky frames" with the total frames rendered while the widget is in that state. This class + * can be used by library widgets to provide the system with more detailed information about + * where jank is happening for diagnostic purposes. + */ +@FlaggedApi(Flags.FLAG_DETAILED_APP_JANK_METRICS_API) +public final class AppJankStats { + // UID of the app + private int mUid; + + // The id that has been set for the widget. + private String mWidgetId; + + // A general category that the widget applies to. + private String mWidgetCategory; + + // The states that the UI elements can report + private String mWidgetState; + + // The number of frames reported during this state. + private long mTotalFrames; + + // Total number of frames determined to be janky during the reported state. + private long mJankyFrames; + + // Histogram of frame duration overruns encoded in predetermined buckets. + private FrameOverrunHistogram mFrameOverrunHistogram; + + + /** Used to indicate no widget category has been set. */ + public static final String WIDGET_CATEGORY_UNSPECIFIED = + "widget_category_unspecified"; + + /** UI elements that facilitate scrolling. */ + public static final String SCROLL = "scroll"; + + /** UI elements that facilitate playing animations. */ + public static final String ANIMATION = "animation"; + + /** UI elements that facilitate media playback. */ + public static final String MEDIA = "media"; + + /** UI elements that facilitate in-app navigation. */ + public static final String NAVIGATION = "navigation"; + + /** UI elements that facilitate displaying, hiding or interacting with keyboard. */ + public static final String KEYBOARD = "keyboard"; + + /** UI elements that facilitate predictive back gesture navigation. */ + public static final String PREDICTIVE_BACK = "predictive_back"; + + /** UI elements that don't fall in one or any of the other categories. */ + public static final String OTHER = "other"; + + /** Used to indicate no widget state has been set. */ + public static final String WIDGET_STATE_UNSPECIFIED = "widget_state_unspecified"; + + /** Used to indicate the UI element currently has no state and is idle. */ + public static final String NONE = "none"; + + /** Used to indicate the UI element is currently scrolling. */ + public static final String SCROLLING = "scrolling"; + + /** Used to indicate the UI element is currently being flung. */ + public static final String FLINGING = "flinging"; + + /** Used to indicate the UI element is currently being swiped. */ + public static final String SWIPING = "swiping"; + + /** Used to indicate the UI element is currently being dragged. */ + public static final String DRAGGING = "dragging"; + + /** Used to indicate the UI element is currently zooming. */ + public static final String ZOOMING = "zooming"; + + /** Used to indicate the UI element is currently animating. */ + public static final String ANIMATING = "animating"; + + /** Used to indicate the UI element is currently playing media. */ + public static final String PLAYBACK = "playback"; + + /** Used to indicate the UI element is currently being tapped on, for example on a keyboard. */ + public static final String TAPPING = "tapping"; + + + /** + * @hide + */ + @StringDef(value = { + WIDGET_CATEGORY_UNSPECIFIED, + SCROLL, + ANIMATION, + MEDIA, + NAVIGATION, + KEYBOARD, + PREDICTIVE_BACK, + OTHER + }) + @Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) + @Retention(RetentionPolicy.SOURCE) + public @interface WidgetCategory { + } + /** + * @hide + */ + @StringDef(value = { + WIDGET_STATE_UNSPECIFIED, + NONE, + SCROLLING, + FLINGING, + SWIPING, + DRAGGING, + ZOOMING, + ANIMATING, + PLAYBACK, + TAPPING, + }) + @Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) + @Retention(RetentionPolicy.SOURCE) + public @interface WidgetState { + } + + + /** + * Creates a new AppJankStats object. + * + * @param appUid the Uid of the App that is collecting jank stats. + * @param widgetId the widget id that frames will be associated to. + * @param widgetCategory a general functionality category that the widget falls into. Must be + * one of the following: SCROLL, ANIMATION, MEDIA, NAVIGATION, KEYBOARD, + * PREDICTIVE_BACK, OTHER or will be set to WIDGET_CATEGORY_UNSPECIFIED + * if no value is passed. + * @param widgetState the state the widget was in while frames were counted. Must be one of + * the following: NONE, SCROLLING, FLINGING, SWIPING, DRAGGING, ZOOMING, + * ANIMATING, PLAYBACK, TAPPING or will be set to WIDGET_STATE_UNSPECIFIED + * if no value is passed. + * @param totalFrames the total number of frames that were counted for this stat. + * @param jankyFrames the total number of janky frames that were counted for this stat. + * @param frameOverrunHistogram the histogram with predefined buckets. See + * {@link #getFrameOverrunHistogram()} for details. + * + */ + public AppJankStats(int appUid, @NonNull String widgetId, + @Nullable @WidgetCategory String widgetCategory, + @Nullable @WidgetState String widgetState, long totalFrames, long jankyFrames, + @NonNull FrameOverrunHistogram frameOverrunHistogram) { + mUid = appUid; + mWidgetId = widgetId; + mWidgetCategory = widgetCategory != null ? widgetCategory : WIDGET_CATEGORY_UNSPECIFIED; + mWidgetState = widgetState != null ? widgetState : WIDGET_STATE_UNSPECIFIED; + mTotalFrames = totalFrames; + mJankyFrames = jankyFrames; + mFrameOverrunHistogram = frameOverrunHistogram; + } + + /** + * Returns the app uid. + * + * @return the app uid. + */ + public int getUid() { + return mUid; + } + + /** + * Returns the id of the widget that reported state changes. + * + * @return the id of the widget that reported state changes. This value cannot be null. + */ + public @NonNull String getWidgetId() { + return mWidgetId; + } + + /** + * Returns the category that the widget's functionality generally falls into, or + * widget_category_unspecified {@link #WIDGET_CATEGORY_UNSPECIFIED} if no value was passed in. + * + * @return the category that the widget's functionality generally falls into, this value cannot + * be null. + */ + public @NonNull @WidgetCategory String getWidgetCategory() { + return mWidgetCategory; + } + + /** + * Returns the widget's state that was reported for this stat, or widget_state_unspecified + * {@link #WIDGET_STATE_UNSPECIFIED} if no value was passed in. + * + * @return the widget's state that was reported for this stat. This value cannot be null. + */ + public @NonNull @WidgetState String getWidgetState() { + return mWidgetState; + } + + /** + * Returns the number of frames that were determined to be janky for this stat. + * + * @return the number of frames that were determined to be janky for this stat. + */ + public long getJankyFrameCount() { + return mJankyFrames; + } + + /** + * Returns the total number of frames counted for this stat. + * + * @return the total number of frames counted for this stat. + */ + public long getTotalFrameCount() { + return mTotalFrames; + } + + /** + * Returns a Histogram containing frame overrun times in millis grouped into predefined buckets. + * See {@link FrameOverrunHistogram} for more information. + * + * @return Histogram containing frame overrun times in predefined buckets. This value cannot + * be null. + */ + public @NonNull FrameOverrunHistogram getFrameOverrunHistogram() { + return mFrameOverrunHistogram; + } +} diff --git a/core/java/android/app/jank/FrameOverrunHistogram.java b/core/java/android/app/jank/FrameOverrunHistogram.java new file mode 100644 index 000000000000..e28ac126a90a --- /dev/null +++ b/core/java/android/app/jank/FrameOverrunHistogram.java @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.jank; + +import android.annotation.FlaggedApi; +import android.annotation.NonNull; + +import java.util.Arrays; + +/** + * This class is intended to be used when reporting {@link AppJankStats} back to the system. It's + * intended to be used by library widgets to help facilitate the reporting of frame overrun times + * by adding those times into predefined buckets. + */ +@FlaggedApi(Flags.FLAG_DETAILED_APP_JANK_METRICS_API) +public class FrameOverrunHistogram { + private static int[] sBucketEndpoints = new int[]{ + Integer.MIN_VALUE, -200, -150, -100, -90, -80, -70, -60, -50, -40, -30, -25, -20, -18, + -16, -14, -12, -10, -8, -6, -4, -2, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 25, 30, 40, + 50, 60, 70, 80, 90, 100, 150, 200, 300, 400, 500, 600, 700, 800, 900, 1000 + }; + private int[] mBucketCounts; + + /** + * Create a new instance of FrameOverrunHistogram. + */ + public FrameOverrunHistogram() { + mBucketCounts = new int[sBucketEndpoints.length - 1]; + } + + /** + * Increases the count by one for the bucket representing the frame overrun duration. + * + * @param frameOverrunMillis frame overrun duration in millis, frame overrun is the difference + * between a frames deadline and when it was rendered. + */ + public void addFrameOverrunMillis(int frameOverrunMillis) { + int countsIndex = getIndexForCountsFromOverrunTime(frameOverrunMillis); + mBucketCounts[countsIndex]++; + } + + /** + * Returns the counts for the all the frame overrun buckets. + * + * @return an array of integers representing the counts of frame overrun times. This value + * cannot be null. + */ + public @NonNull int[] getBucketCounters() { + return Arrays.copyOf(mBucketCounts, mBucketCounts.length); + } + + /** + * Returns the predefined endpoints for the histogram. + * + * @return array of integers representing the endpoints for the predefined histogram count + * buckets. This value cannot be null. + */ + public @NonNull int[] getBucketEndpointsMillis() { + return Arrays.copyOf(sBucketEndpoints, sBucketEndpoints.length); + } + + // This takes the overrun time and returns what bucket it belongs to in the counters array. + private int getIndexForCountsFromOverrunTime(int overrunTime) { + if (overrunTime < 20) { + if (overrunTime >= -20) { + return (overrunTime + 20) / 2 + 12; + } + if (overrunTime >= -30) { + return (overrunTime + 30) / 5 + 10; + } + if (overrunTime >= -100) { + return (overrunTime + 100) / 10 + 3; + } + if (overrunTime >= -200) { + return (overrunTime + 200) / 50 + 1; + } + return 0; + } + if (overrunTime < 30) { + return (overrunTime - 20) / 5 + 32; + } + if (overrunTime < 100) { + return (overrunTime - 30) / 10 + 34; + } + if (overrunTime < 200) { + return (overrunTime - 50) / 100 + 41; + } + if (overrunTime < 1000) { + return (overrunTime - 200) / 100 + 43; + } + return sBucketEndpoints.length - 1; + } +} diff --git a/core/java/android/app/jank/JankDataProcessor.java b/core/java/android/app/jank/JankDataProcessor.java index 981a9167c2da..3783a5f9e829 100644 --- a/core/java/android/app/jank/JankDataProcessor.java +++ b/core/java/android/app/jank/JankDataProcessor.java @@ -87,6 +87,14 @@ public class JankDataProcessor { } /** + * Merges app jank stats reported by components outside the platform to the current pending + * stats + */ + public void mergeJankStats(AppJankStats jankStats, String activityName) { + // TODO b/377572463 Add Merging Logic + } + + /** * Returns the aggregate map of different pending jank stats. */ @VisibleForTesting diff --git a/core/java/android/app/jank/JankTracker.java b/core/java/android/app/jank/JankTracker.java index df422e0069c5..202281f98c97 100644 --- a/core/java/android/app/jank/JankTracker.java +++ b/core/java/android/app/jank/JankTracker.java @@ -84,6 +84,14 @@ public class JankTracker { registerWindowListeners(); } + /** + * Merges app jank stats reported by components outside the platform to the current pending + * stats + */ + public void mergeAppJankStats(AppJankStats appJankStats) { + mJankDataProcessor.mergeJankStats(appJankStats, mActivityName); + } + public void setActivityName(@NonNull String activityName) { mActivityName = activityName; } diff --git a/core/java/android/app/multitasking.aconfig b/core/java/android/app/multitasking.aconfig index 9a645192a155..c8455c1f439f 100644 --- a/core/java/android/app/multitasking.aconfig +++ b/core/java/android/app/multitasking.aconfig @@ -8,3 +8,11 @@ flag { description: "Enables PiP UI state callback on entering" bug: "303718131" } + +flag { + name: "enable_tv_implicit_enter_pip_restriction" + is_exported: true + namespace: "tv_system_ui" + description: "Enables restrictions to PiP entry on TV for setAutoEnterEnabled and lifecycle methods" + bug: "283115999" +} diff --git a/core/java/android/app/notification.aconfig b/core/java/android/app/notification.aconfig index ee93870be055..6934e9883840 100644 --- a/core/java/android/app/notification.aconfig +++ b/core/java/android/app/notification.aconfig @@ -100,16 +100,6 @@ flag { } flag { - name: "visit_person_uri" - namespace: "systemui" - description: "Guards the security fix that ensures all URIs Person.java are valid" - bug: "281044385" - metadata { - purpose: PURPOSE_BUGFIX - } -} - -flag { name: "notification_expansion_optional" namespace: "systemui" description: "Experiment to restore the pre-S behavior where standard notifications are not expandable unless they have actions." diff --git a/core/java/android/app/performance.aconfig b/core/java/android/app/performance.aconfig index f51f748bc71f..61b53f97fea1 100644 --- a/core/java/android/app/performance.aconfig +++ b/core/java/android/app/performance.aconfig @@ -18,3 +18,20 @@ flag { description: "Enforce PropertyInvalidatedCache.setTestMode() protocol" bug: "360897450" } + +flag { + namespace: "system_performance" + name: "pic_isolate_cache_by_uid" + is_fixed_read_only: true + description: "Ensure that different UIDs use different caches" + bug: "373752556" +} + +flag { + namespace: "system_performance" + name: "pic_isolated_cache_statistics" + is_fixed_read_only: true + description: "Collects statistics for cache UID isolation strategies" + bug: "373752556" +} + diff --git a/core/java/android/app/supervision/flags.aconfig b/core/java/android/app/supervision/flags.aconfig index bcb5b3636c95..d5e696d49ff4 100644 --- a/core/java/android/app/supervision/flags.aconfig +++ b/core/java/android/app/supervision/flags.aconfig @@ -7,4 +7,12 @@ flag { namespace: "supervision" description: "Flag to enable the SupervisionService" bug: "340351729" -}
\ No newline at end of file +} + +flag { + name: "supervision_api_on_wear" + is_exported: true + namespace: "supervision" + description: "Flag to enable the SupervisionService on Wear devices" + bug: "373358935" +} diff --git a/core/java/android/app/wallpaper.aconfig b/core/java/android/app/wallpaper.aconfig index 4b880d030413..f750a844f4ff 100644 --- a/core/java/android/app/wallpaper.aconfig +++ b/core/java/android/app/wallpaper.aconfig @@ -22,3 +22,21 @@ flag { bug: "347235611" is_exported: true } + +flag { + name: "customization_packs_apis" + is_exported: true + namespace: "systemui" + description: "Move APIs related to bitmap and crops to @SystemApi." + bug: "372344184" +} + +flag { + name: "accurate_wallpaper_downsampling" + namespace: "systemui" + description: "Accurate downsampling of wallpaper bitmap for high resolution images" + bug: "355665230" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/core/java/android/app/wallpaper/WallpaperDescription.java b/core/java/android/app/wallpaper/WallpaperDescription.java index 8ffda7242b37..4a142bb5287a 100644 --- a/core/java/android/app/wallpaper/WallpaperDescription.java +++ b/core/java/android/app/wallpaper/WallpaperDescription.java @@ -113,7 +113,7 @@ public final class WallpaperDescription implements Parcelable { /** @return the description for this wallpaper */ @NonNull public List<CharSequence> getDescription() { - return new ArrayList<>(); + return mDescription; } /** @return the {@link Uri} for the action associated with the wallpaper, or {@code null} if not diff --git a/core/java/android/content/ClipData.java b/core/java/android/content/ClipData.java index ff0bb25bbccc..cc57dc05d6b1 100644 --- a/core/java/android/content/ClipData.java +++ b/core/java/android/content/ClipData.java @@ -398,6 +398,7 @@ public class ClipData implements Parcelable { * Retrieve the raw Intent contained in this Item. */ public Intent getIntent() { + Intent.maybeMarkAsMissingCreatorToken(mIntent); return mIntent; } diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 186f7b3e111c..6086f2455a31 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -6802,6 +6802,12 @@ public abstract class Context { public static final String MEDIA_QUALITY_SERVICE = "media_quality"; /** + * Service to perform operations needed for dynamic instrumentation. + * @hide + */ + public static final String DYNAMIC_INSTRUMENTATION_SERVICE = "dynamic_instrumentation"; + + /** * Determine whether the given permission is allowed for a particular * process and user ID running in the system. * diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 6fa5a9b82858..88533049f970 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -108,6 +108,7 @@ import java.util.Locale; import java.util.Objects; import java.util.Set; import java.util.TimeZone; +import java.util.function.Consumer; /** * An intent is an abstract description of an operation to be performed. It @@ -892,6 +893,20 @@ public class Intent implements Parcelable, Cloneable { public static void maybeMarkAsMissingCreatorToken(Object object) { if (object instanceof Intent intent) { maybeMarkAsMissingCreatorTokenInternal(intent); + } else if (object instanceof Parcelable[] parcelables) { + for (Parcelable p : parcelables) { + if (p instanceof Intent intent) { + maybeMarkAsMissingCreatorTokenInternal(intent); + } + } + } else if (object instanceof ArrayList parcelables) { + int N = parcelables.size(); + for (int i = 0; i < N; i++) { + Object p = parcelables.get(i); + if (p instanceof Intent intent) { + maybeMarkAsMissingCreatorTokenInternal(intent); + } + } } } @@ -12204,7 +12219,68 @@ public class Intent implements Parcelable, Cloneable { // Stores a creator token for an intent embedded as an extra intent in a top level intent, private IBinder mCreatorToken; // Stores all extra keys whose values are intents for a top level intent. - private ArraySet<String> mExtraIntentKeys; + private ArraySet<NestedIntentKey> mNestedIntentKeys; + } + + /** + * @hide + */ + public static class NestedIntentKey { + /** @hide */ + @IntDef(flag = true, prefix = {"NESTED_INTENT_KEY_TYPE"}, value = { + NESTED_INTENT_KEY_TYPE_EXTRA_PARCEL, + NESTED_INTENT_KEY_TYPE_EXTRA_PARCEL_ARRAY, + NESTED_INTENT_KEY_TYPE_EXTRA_PARCEL_LIST, + NESTED_INTENT_KEY_TYPE_CLIP_DATA, + }) + @Retention(RetentionPolicy.SOURCE) + private @interface NestedIntentKeyType { + } + + /** + * This flag indicates the key is for an extra parcel in mExtras. + */ + private static final int NESTED_INTENT_KEY_TYPE_EXTRA_PARCEL = 1 << 0; + + /** + * This flag indicates the key is for an extra parcel array in mExtras and the index is the + * index of that array. + */ + private static final int NESTED_INTENT_KEY_TYPE_EXTRA_PARCEL_ARRAY = 1 << 1; + + /** + * This flag indicates the key is for an extra parcel list in mExtras and the index is the + * index of that list. + */ + private static final int NESTED_INTENT_KEY_TYPE_EXTRA_PARCEL_LIST = 1 << 2; + + /** + * This flag indicates the key is for an extra parcel in mClipData.mItems. + */ + private static final int NESTED_INTENT_KEY_TYPE_CLIP_DATA = 1 << 3; + + private final @NestedIntentKeyType int mType; + private final String mKey; + private final int mIndex; + + private NestedIntentKey(@NestedIntentKeyType int type, String key, int index) { + this.mType = type; + this.mKey = key; + this.mIndex = index; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + NestedIntentKey that = (NestedIntentKey) o; + return mType == that.mType && mIndex == that.mIndex && Objects.equals(mKey, that.mKey); + } + + @Override + public int hashCode() { + return Objects.hash(mType, mKey, mIndex); + } } private @Nullable CreatorTokenInfo mCreatorTokenInfo; @@ -12227,8 +12303,8 @@ public class Intent implements Parcelable, Cloneable { } /** @hide */ - public Set<String> getExtraIntentKeys() { - return mCreatorTokenInfo == null ? null : mCreatorTokenInfo.mExtraIntentKeys; + public Set<NestedIntentKey> getExtraIntentKeys() { + return mCreatorTokenInfo == null ? null : mCreatorTokenInfo.mNestedIntentKeys; } /** @hide */ @@ -12246,45 +12322,178 @@ public class Intent implements Parcelable, Cloneable { * @hide */ public void collectExtraIntentKeys() { - if (!preventIntentRedirect()) return; + if (preventIntentRedirect()) { + collectNestedIntentKeysRecur(new ArraySet<>()); + } + } - if (mExtras != null && !mExtras.isEmpty()) { + private void collectNestedIntentKeysRecur(Set<Intent> visited) { + if (mExtras != null && !mExtras.isParcelled() && !mExtras.isEmpty()) { for (String key : mExtras.keySet()) { - if (mExtras.get(key) instanceof Intent) { - if (mCreatorTokenInfo == null) { - mCreatorTokenInfo = new CreatorTokenInfo(); - } - if (mCreatorTokenInfo.mExtraIntentKeys == null) { - mCreatorTokenInfo.mExtraIntentKeys = new ArraySet<>(); - } - mCreatorTokenInfo.mExtraIntentKeys.add(key); + Object value = mExtras.get(key); + + if (value instanceof Intent intent && !visited.contains(intent)) { + handleNestedIntent(intent, visited, new NestedIntentKey( + NestedIntentKey.NESTED_INTENT_KEY_TYPE_EXTRA_PARCEL, key, 0)); + } else if (value instanceof Parcelable[] parcelables) { + handleParcelableArray(parcelables, key, visited); + } else if (value instanceof ArrayList<?> parcelables) { + handleParcelableList(parcelables, key, visited); } } } + + if (mClipData != null) { + for (int i = 0; i < mClipData.getItemCount(); i++) { + Intent intent = mClipData.getItemAt(i).mIntent; + if (intent != null && !visited.contains(intent)) { + handleNestedIntent(intent, visited, new NestedIntentKey( + NestedIntentKey.NESTED_INTENT_KEY_TYPE_CLIP_DATA, null, i)); + } + } + } + } + + private void handleNestedIntent(Intent intent, Set<Intent> visited, NestedIntentKey key) { + visited.add(intent); + if (mCreatorTokenInfo == null) { + mCreatorTokenInfo = new CreatorTokenInfo(); + } + if (mCreatorTokenInfo.mNestedIntentKeys == null) { + mCreatorTokenInfo.mNestedIntentKeys = new ArraySet<>(); + } + mCreatorTokenInfo.mNestedIntentKeys.add(key); + intent.collectNestedIntentKeysRecur(visited); + } + + private void handleParcelableArray(Parcelable[] parcelables, String key, Set<Intent> visited) { + for (int i = 0; i < parcelables.length; i++) { + if (parcelables[i] instanceof Intent intent && !visited.contains(intent)) { + handleNestedIntent(intent, visited, new NestedIntentKey( + NestedIntentKey.NESTED_INTENT_KEY_TYPE_EXTRA_PARCEL_ARRAY, key, i)); + } + } } + private void handleParcelableList(ArrayList<?> parcelables, String key, Set<Intent> visited) { + for (int i = 0; i < parcelables.size(); i++) { + if (parcelables.get(i) instanceof Intent intent && !visited.contains(intent)) { + handleNestedIntent(intent, visited, new NestedIntentKey( + NestedIntentKey.NESTED_INTENT_KEY_TYPE_EXTRA_PARCEL_LIST, key, i)); + } + } + } + + private static final Consumer<Intent> MARK_TRUSTED_TOKEN_PRESENT_ACTION = intent -> { + intent.mLocalFlags |= LOCAL_FLAG_TRUSTED_CREATOR_TOKEN_PRESENT; + }; + + private static final Consumer<Intent> ENABLE_TOKEN_VERIFY_ACTION = intent -> { + if (intent.mExtras != null) { + intent.mExtras.enableTokenVerification(); + } + }; + /** @hide */ public void checkCreatorToken() { - if (mExtras == null) return; - if (mCreatorTokenInfo != null && mCreatorTokenInfo.mExtraIntentKeys != null) { - for (String key : mCreatorTokenInfo.mExtraIntentKeys) { - try { - Intent extraIntent = mExtras.getParcelable(key, Intent.class); - if (extraIntent == null) { - Log.w(TAG, "The key {" + key - + "} does not correspond to an intent in the bundle."); - continue; + forEachNestedCreatorToken(MARK_TRUSTED_TOKEN_PRESENT_ACTION, ENABLE_TOKEN_VERIFY_ACTION); + if (mExtras != null) { + // mark the bundle as intent extras after calls to getParcelable. + // otherwise, the logic to mark missing token would run before + // mark trusted creator token present. + mExtras.enableTokenVerification(); + } + } + + /** @hide */ + public void forEachNestedCreatorToken(Consumer<? super Intent> action) { + forEachNestedCreatorToken(action, null); + } + + private void forEachNestedCreatorToken(Consumer<? super Intent> action, + Consumer<? super Intent> postAction) { + if (mExtras == null && mClipData == null) return; + + if (mCreatorTokenInfo != null && mCreatorTokenInfo.mNestedIntentKeys != null) { + int N = mCreatorTokenInfo.mNestedIntentKeys.size(); + for (int i = 0; i < N; i++) { + NestedIntentKey key = mCreatorTokenInfo.mNestedIntentKeys.valueAt(i); + Intent extraIntent = extractIntentFromKey(key); + + if (extraIntent != null) { + action.accept(extraIntent); + extraIntent.forEachNestedCreatorToken(action); + if (postAction != null) { + postAction.accept(extraIntent); } - extraIntent.mLocalFlags |= LOCAL_FLAG_TRUSTED_CREATOR_TOKEN_PRESENT; - } catch (Exception e) { - Log.e(TAG, "Failed to validate creator token. key: " + key + ".", e); + } else { + Log.w(TAG, getLogMessageForKey(key)); } } } - // mark the bundle as intent extras after calls to getParcelable. - // otherwise, the logic to mark missing token would run before - // mark trusted creator token present. - mExtras.setIsIntentExtra(); + } + + private Intent extractIntentFromKey(NestedIntentKey key) { + switch (key.mType) { + case NestedIntentKey.NESTED_INTENT_KEY_TYPE_EXTRA_PARCEL: + return mExtras == null ? null : mExtras.getParcelable(key.mKey, Intent.class); + case NestedIntentKey.NESTED_INTENT_KEY_TYPE_EXTRA_PARCEL_ARRAY: + if (mExtras == null) return null; + Intent[] extraIntents = mExtras.getParcelableArray(key.mKey, Intent.class); + if (extraIntents != null && key.mIndex < extraIntents.length) { + return extraIntents[key.mIndex]; + } + break; + case NestedIntentKey.NESTED_INTENT_KEY_TYPE_EXTRA_PARCEL_LIST: + if (mExtras == null) return null; + ArrayList<Intent> extraIntentsList = mExtras.getParcelableArrayList(key.mKey, + Intent.class); + if (extraIntentsList != null && key.mIndex < extraIntentsList.size()) { + return extraIntentsList.get(key.mIndex); + } + break; + case NestedIntentKey.NESTED_INTENT_KEY_TYPE_CLIP_DATA: + if (mClipData == null) return null; + if (key.mIndex < mClipData.getItemCount()) { + ClipData.Item item = mClipData.getItemAt(key.mIndex); + if (item != null) { + return item.mIntent; + } + } + break; + } + return null; + } + + private String getLogMessageForKey(NestedIntentKey key) { + switch (key.mType) { + case NestedIntentKey.NESTED_INTENT_KEY_TYPE_EXTRA_PARCEL: + return "The key {" + key + "} does not correspond to an intent in the bundle."; + case NestedIntentKey.NESTED_INTENT_KEY_TYPE_EXTRA_PARCEL_ARRAY: + if (mExtras.getParcelableArray(key.mKey, Intent.class) == null) { + return "The key {" + key + + "} does not correspond to a Parcelable[] in the bundle."; + } else { + return "Parcelable[" + key.mIndex + "] for key {" + key + "} is not an intent."; + } + case NestedIntentKey.NESTED_INTENT_KEY_TYPE_EXTRA_PARCEL_LIST: + if (mExtras.getParcelableArrayList(key.mKey, Intent.class) == null) { + return "The key {" + key + + "} does not correspond to an ArrayList<Parcelable> in the bundle."; + } else { + return "List.get(" + key.mIndex + ") for key {" + key + "} is not an intent."; + } + case NestedIntentKey.NESTED_INTENT_KEY_TYPE_CLIP_DATA: + if (key.mIndex >= mClipData.getItemCount()) { + return "Index out of range for clipData items. index: " + key.mIndex + + ". item counts: " + mClipData.getItemCount(); + } else { + return "clipData items at index [" + key.mIndex + + "] is null or does not contain an intent."; + } + default: + return "Unknown key type: " + key.mType; + } } /** @@ -12357,7 +12566,19 @@ public class Intent implements Parcelable, Cloneable { } else { out.writeInt(1); out.writeStrongBinder(mCreatorTokenInfo.mCreatorToken); - out.writeArraySet(mCreatorTokenInfo.mExtraIntentKeys); + + if (mCreatorTokenInfo.mNestedIntentKeys != null) { + final int N = mCreatorTokenInfo.mNestedIntentKeys.size(); + out.writeInt(N); + for (int i = 0; i < N; i++) { + NestedIntentKey key = mCreatorTokenInfo.mNestedIntentKeys.valueAt(i); + out.writeInt(key.mType); + out.writeString8(key.mKey); + out.writeInt(key.mIndex); + } + } else { + out.writeInt(0); + } } } } @@ -12422,7 +12643,18 @@ public class Intent implements Parcelable, Cloneable { if (in.readInt() != 0) { mCreatorTokenInfo = new CreatorTokenInfo(); mCreatorTokenInfo.mCreatorToken = in.readStrongBinder(); - mCreatorTokenInfo.mExtraIntentKeys = (ArraySet<String>) in.readArraySet(null); + + N = in.readInt(); + if (N > 0) { + mCreatorTokenInfo.mNestedIntentKeys = new ArraySet<>(N); + for (int i = 0; i < N; i++) { + int type = in.readInt(); + String key = in.readString8(); + int index = in.readInt(); + mCreatorTokenInfo.mNestedIntentKeys.append( + new NestedIntentKey(type, key, index)); + } + } } } } diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index 54c5596623a2..63279af8480d 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -2936,6 +2936,8 @@ public class PackageInstaller { public @Nullable String dexoptCompilerFilter = null; /** {@hide} */ public boolean forceVerification; + /** {@hide} */ + public boolean isAutoInstallDependenciesEnabled = true; private final ArrayMap<String, Integer> mPermissionStates; @@ -2991,6 +2993,7 @@ public class PackageInstaller { unarchiveId = source.readInt(); dexoptCompilerFilter = source.readString(); forceVerification = source.readBoolean(); + isAutoInstallDependenciesEnabled = source.readBoolean(); } /** {@hide} */ @@ -3028,6 +3031,7 @@ public class PackageInstaller { ret.unarchiveId = unarchiveId; ret.dexoptCompilerFilter = dexoptCompilerFilter; ret.forceVerification = forceVerification; + ret.isAutoInstallDependenciesEnabled = isAutoInstallDependenciesEnabled; return ret; } @@ -3744,6 +3748,23 @@ public class PackageInstaller { this.forceVerification = true; } + /** + * Optionally indicate whether missing SDK or static shared library dependencies should be + * automatically fetched and installed when installing an app that wants to use these + * dependencies. + * + * <p> This feature is enabled by default. + * + * @param enableAutoInstallDependencies {@code true} to enable auto-installation of missing + * SDK or static shared library dependencies, + * {@code false} to disable and fail immediately if + * dependencies aren't already installed. + */ + @FlaggedApi(Flags.FLAG_SDK_DEPENDENCY_INSTALLER) + public void setEnableAutoInstallDependencies(boolean enableAutoInstallDependencies) { + isAutoInstallDependenciesEnabled = enableAutoInstallDependencies; + } + /** {@hide} */ public void dump(IndentingPrintWriter pw) { pw.printPair("mode", mode); @@ -3780,6 +3801,7 @@ public class PackageInstaller { pw.printPair("unarchiveId", unarchiveId); pw.printPair("dexoptCompilerFilter", dexoptCompilerFilter); pw.printPair("forceVerification", forceVerification); + pw.printPair("isAutoInstallDependenciesEnabled", isAutoInstallDependenciesEnabled); pw.println(); } @@ -3827,6 +3849,7 @@ public class PackageInstaller { dest.writeInt(unarchiveId); dest.writeString(dexoptCompilerFilter); dest.writeBoolean(forceVerification); + dest.writeBoolean(isAutoInstallDependenciesEnabled); } public static final Parcelable.Creator<SessionParams> @@ -4005,6 +4028,9 @@ public class PackageInstaller { private String mSessionErrorMessage; /** {@hide} */ + public boolean isAutoInstallingDependenciesEnabled; + + /** {@hide} */ public boolean isCommitted; /** {@hide} */ @@ -4097,6 +4123,7 @@ public class PackageInstaller { packageSource = source.readInt(); applicationEnabledSettingPersistent = source.readBoolean(); pendingUserActionReason = source.readInt(); + isAutoInstallingDependenciesEnabled = source.readBoolean(); } /** @@ -4681,6 +4708,16 @@ public class PackageInstaller { return (installFlags & PackageManager.INSTALL_UNARCHIVE) != 0; } + /** + * Check whether missing SDK or static shared library dependencies should be automatically + * fetched and installed when installing an app that wants to use these dependencies. + * + * @return true if the dependencies will be auto-installed, false otherwise. + */ + @FlaggedApi(Flags.FLAG_SDK_DEPENDENCY_INSTALLER) + public boolean isAutoInstallDependenciesEnabled() { + return isAutoInstallingDependenciesEnabled; + } @Override public int describeContents() { @@ -4735,6 +4772,7 @@ public class PackageInstaller { dest.writeInt(packageSource); dest.writeBoolean(applicationEnabledSettingPersistent); dest.writeInt(pendingUserActionReason); + dest.writeBoolean(isAutoInstallingDependenciesEnabled); } public static final Parcelable.Creator<SessionInfo> diff --git a/core/java/android/content/pm/flags.aconfig b/core/java/android/content/pm/flags.aconfig index c424229f479b..9ba5a352358b 100644 --- a/core/java/android/content/pm/flags.aconfig +++ b/core/java/android/content/pm/flags.aconfig @@ -321,6 +321,7 @@ flag { flag { name: "sdk_dependency_installer" + is_exported: true namespace: "package_manager_service" description: "Feature flag to enable installation of missing sdk dependency of app" bug: "370822870" @@ -358,3 +359,10 @@ flag { bug: "377474232" is_fixed_read_only: true } + +flag { + name: "support_minor_versions_in_minsdkversion" + namespace: "package_manager_service" + description: "Block app installations that specify an incompatible minor SDK version" + bug: "377474232" +} diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig index 3d89ce12dec4..813208d7ff38 100644 --- a/core/java/android/content/pm/multiuser.aconfig +++ b/core/java/android/content/pm/multiuser.aconfig @@ -330,6 +330,17 @@ flag { is_fixed_read_only: true } +flag { + name: "cache_user_info_read_only" + namespace: "multiuser" + description: "Cache UserInfo to avoid unnecessary binder calls" + bug: "161915546" + 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/ApkLiteParseUtils.java b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java index 34c3f5798bc5..e9e8578af787 100644 --- a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java +++ b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java @@ -38,6 +38,7 @@ import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.AttributeSet; +import android.util.EmptyArray; import android.util.Pair; import android.util.Slog; @@ -565,10 +566,7 @@ public class ApkLiteParseUtils { usesSdkLibrariesVersionsMajor, usesSdkLibVersionMajor, /*allowDuplicates=*/ true); - // We allow ":" delimiters in the SHA declaration as this is the format - // emitted by the certtool making it easy for developers to copy/paste. - // TODO(372862145): Add test for this replacement - usesSdkCertDigest = usesSdkCertDigest.replace(":", "").toLowerCase(); + usesSdkCertDigest = normalizeCertDigest(usesSdkCertDigest); if ("".equals(usesSdkCertDigest)) { // Test-only uses-sdk-library empty certificate digest override. @@ -618,18 +616,23 @@ public class ApkLiteParseUtils { usesStaticLibrariesVersions, usesStaticLibVersion, /*allowDuplicates=*/ true); - // We allow ":" delimiters in the SHA declaration as this is the format - // emitted by the certtool making it easy for developers to copy/paste. - // TODO(372862145): Add test for this replacement - usesStaticLibCertDigest = - usesStaticLibCertDigest.replace(":", "").toLowerCase(); + usesStaticLibCertDigest = normalizeCertDigest(usesStaticLibCertDigest); + + ParseResult<String[]> certResult = + parseAdditionalCertificates(input, parser); + if (certResult.isError()) { + return input.error(certResult); + } + String[] additionalCertSha256Digests = certResult.getResult(); + String[] certSha256Digests = + new String[additionalCertSha256Digests.length + 1]; + certSha256Digests[0] = usesStaticLibCertDigest; + System.arraycopy(additionalCertSha256Digests, 0, certSha256Digests, + 1, additionalCertSha256Digests.length); - // TODO(372862145): Add support for multiple signer for app targeting - // O-MR1 usesStaticLibrariesCertDigests = ArrayUtils.appendElement( String[].class, usesStaticLibrariesCertDigests, - new String[]{usesStaticLibCertDigest}, - /*allowDuplicates=*/ true); + certSha256Digests, /*allowDuplicates=*/ true); break; case TAG_SDK_LIBRARY: isSdkLibrary = true; @@ -809,6 +812,43 @@ public class ApkLiteParseUtils { declaredLibraries)); } + private static ParseResult<String[]> parseAdditionalCertificates(ParseInput input, + XmlResourceParser parser) throws XmlPullParserException, IOException { + String[] certSha256Digests = EmptyArray.STRING; + final int depth = parser.getDepth(); + int type; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG + || parser.getDepth() > depth)) { + if (type != XmlPullParser.START_TAG) { + continue; + } + final String nodeName = parser.getName(); + if (nodeName.equals("additional-certificate")) { + String certSha256Digest = parser.getAttributeValue( + ANDROID_RES_NAMESPACE, "certDigest"); + if (TextUtils.isEmpty(certSha256Digest)) { + return input.error("Bad additional-certificate declaration with empty" + + " certDigest:" + certSha256Digest); + } + + certSha256Digest = normalizeCertDigest(certSha256Digest); + certSha256Digests = ArrayUtils.appendElement(String.class, + certSha256Digests, certSha256Digest); + } + } + + return input.success(certSha256Digests); + } + + /** + * We allow ":" delimiters in the SHA declaration as this is the format emitted by the + * certtool making it easy for developers to copy/paste. + */ + private static String normalizeCertDigest(String certDigest) { + return certDigest.replace(":", "").toLowerCase(); + } + private static boolean isDeviceAdminReceiver( XmlResourceParser parser, boolean applicationHasBindDeviceAdminPermission) throws XmlPullParserException, IOException { diff --git a/core/java/android/content/res/ApkAssets.java b/core/java/android/content/res/ApkAssets.java index 68b5d782bfbf..908999b64961 100644 --- a/core/java/android/content/res/ApkAssets.java +++ b/core/java/android/content/res/ApkAssets.java @@ -124,11 +124,13 @@ public final class ApkAssets { @Nullable @GuardedBy("this") - private final StringBlock mStringBlock; // null or closed if mNativePtr = 0. + private StringBlock mStringBlock; // null or closed if mNativePtr = 0. @PropertyFlags private final int mFlags; + private final boolean mIsOverlay; + @Nullable private final AssetsProvider mAssets; @@ -302,40 +304,43 @@ public final class ApkAssets { private ApkAssets(@FormatType int format, @NonNull String path, @PropertyFlags int flags, @Nullable AssetsProvider assets) throws IOException { + this(format, flags, assets); Objects.requireNonNull(path, "path"); - mFlags = flags; mNativePtr = nativeLoad(format, path, flags, assets); mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/); - mAssets = assets; } private ApkAssets(@FormatType int format, @NonNull FileDescriptor fd, @NonNull String friendlyName, @PropertyFlags int flags, @Nullable AssetsProvider assets) throws IOException { + this(format, flags, assets); Objects.requireNonNull(fd, "fd"); Objects.requireNonNull(friendlyName, "friendlyName"); - mFlags = flags; mNativePtr = nativeLoadFd(format, fd, friendlyName, flags, assets); mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/); - mAssets = assets; } private ApkAssets(@FormatType int format, @NonNull FileDescriptor fd, @NonNull String friendlyName, long offset, long length, @PropertyFlags int flags, @Nullable AssetsProvider assets) throws IOException { + this(format, flags, assets); Objects.requireNonNull(fd, "fd"); Objects.requireNonNull(friendlyName, "friendlyName"); - mFlags = flags; mNativePtr = nativeLoadFdOffsets(format, fd, friendlyName, offset, length, flags, assets); mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/); - mAssets = assets; } private ApkAssets(@PropertyFlags int flags, @Nullable AssetsProvider assets) { - mFlags = flags; + this(FORMAT_APK, flags, assets); mNativePtr = nativeLoadEmpty(flags, assets); mStringBlock = null; + } + + private ApkAssets(@FormatType int format, @PropertyFlags int flags, + @Nullable AssetsProvider assets) { + mFlags = flags; mAssets = assets; + mIsOverlay = format == FORMAT_IDMAP; } @UnsupportedAppUsage @@ -425,6 +430,18 @@ public final class ApkAssets { } } + public boolean isSystem() { + return (mFlags & PROPERTY_SYSTEM) != 0; + } + + public boolean isSharedLib() { + return (mFlags & PROPERTY_DYNAMIC) != 0; + } + + public boolean isOverlay() { + return mIsOverlay; + } + @Override public String toString() { return "ApkAssets{path=" + getDebugName() + "}"; diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java index e6b93427f413..bcaceb24d767 100644 --- a/core/java/android/content/res/ResourcesImpl.java +++ b/core/java/android/content/res/ResourcesImpl.java @@ -203,9 +203,25 @@ public class ResourcesImpl { @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public ResourcesImpl(@NonNull AssetManager assets, @Nullable DisplayMetrics metrics, @Nullable Configuration config, @NonNull DisplayAdjustments displayAdjustments) { - mAssets = assets; - mAppliedSharedLibsHash = - ResourcesManager.getInstance().updateResourceImplWithRegisteredLibs(this); + // Don't reuse assets by default as we have no control over whether they're already + // inside some other ResourcesImpl. + this(assets, metrics, config, displayAdjustments, false); + } + + public ResourcesImpl(@NonNull ResourcesImpl orig) { + // We know for sure that the other assets are in use, so can't reuse the object here. + this(orig.getAssets(), orig.getMetrics(), orig.getConfiguration(), + orig.getDisplayAdjustments(), false); + } + + public ResourcesImpl(@NonNull AssetManager assets, @Nullable DisplayMetrics metrics, + @Nullable Configuration config, @NonNull DisplayAdjustments displayAdjustments, + boolean reuseAssets) { + final var assetsAndHash = + ResourcesManager.getInstance().updateResourceImplAssetsWithRegisteredLibs(assets, + reuseAssets); + mAssets = assetsAndHash.first; + mAppliedSharedLibsHash = assetsAndHash.second; mMetrics.setToDefaults(); mDisplayAdjustments = displayAdjustments; mConfiguration.setToDefaults(); diff --git a/core/java/android/content/res/flags.aconfig b/core/java/android/content/res/flags.aconfig index 26ecbd1982d5..f23c193e2da0 100644 --- a/core/java/android/content/res/flags.aconfig +++ b/core/java/android/content/res/flags.aconfig @@ -95,3 +95,13 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "layout_readwrite_flags" + is_exported: true + namespace: "resource_manager" + description: "Feature flag for allowing read/write flags in layout files" + bug: "377974898" + # This flag is used to control aapt2 behavior. + is_fixed_read_only: true +} diff --git a/core/java/android/credentials/flags.aconfig b/core/java/android/credentials/flags.aconfig index d2435757756c..6c35d106bfb7 100644 --- a/core/java/android/credentials/flags.aconfig +++ b/core/java/android/credentials/flags.aconfig @@ -3,6 +3,16 @@ container: "system" flag { namespace: "credential_manager" + name: "ttl_fix_enabled" + description: "Enable fix for transaction too large issue" + bug: "371052524" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { + namespace: "credential_manager" name: "settings_activity_enabled" is_exported: true description: "Enable the Credential Manager Settings Activity APIs" diff --git a/core/java/android/database/CursorWindow.java b/core/java/android/database/CursorWindow.java index ef59e0af3a27..93ef5c365730 100644 --- a/core/java/android/database/CursorWindow.java +++ b/core/java/android/database/CursorWindow.java @@ -45,7 +45,7 @@ import dalvik.system.CloseGuard; * </p> */ @RavenwoodKeepWholeClass -@RavenwoodRedirectionClass("CursorWindow_host") +@RavenwoodRedirectionClass("CursorWindow_ravenwood") public class CursorWindow extends SQLiteClosable implements Parcelable { private static final String STATS_TAG = "CursorWindowStats"; diff --git a/ravenwood/runtime-helper-src/framework/android/database/CursorWindow_host.java b/core/java/android/database/CursorWindow_ravenwood.java index e21a9cd71a2d..990ec5e9d59e 100644 --- a/ravenwood/runtime-helper-src/framework/android/database/CursorWindow_host.java +++ b/core/java/android/database/CursorWindow_ravenwood.java @@ -17,6 +17,7 @@ package android.database; import android.database.sqlite.SQLiteException; import android.os.Parcel; +import android.ravenwood.annotation.RavenwoodKeepWholeClass; import android.util.Base64; import java.text.DecimalFormat; @@ -26,9 +27,10 @@ import java.util.Arrays; import java.util.HashMap; import java.util.List; -public class CursorWindow_host { +@RavenwoodKeepWholeClass +class CursorWindow_ravenwood { - private static final HashMap<Long, CursorWindow_host> sInstances = new HashMap<>(); + private static final HashMap<Long, CursorWindow_ravenwood> sInstances = new HashMap<>(); private static long sNextId = 1; private String mName; @@ -41,7 +43,7 @@ public class CursorWindow_host { private final List<Row> mRows = new ArrayList<>(); public static long nativeCreate(String name, int cursorWindowSize) { - CursorWindow_host instance = new CursorWindow_host(); + CursorWindow_ravenwood instance = new CursorWindow_ravenwood(); instance.mName = name; long instanceId = sNextId++; sInstances.put(instanceId, instance); @@ -66,7 +68,7 @@ public class CursorWindow_host { } public static boolean nativeAllocRow(long windowPtr) { - CursorWindow_host instance = sInstances.get(windowPtr); + CursorWindow_ravenwood instance = sInstances.get(windowPtr); Row row = new Row(); row.mFields = new String[instance.mColumnNum]; row.mTypes = new int[instance.mColumnNum]; @@ -76,7 +78,7 @@ public class CursorWindow_host { } private static boolean put(long windowPtr, String value, int type, int row, int column) { - CursorWindow_host instance = sInstances.get(windowPtr); + CursorWindow_ravenwood instance = sInstances.get(windowPtr); if (row >= instance.mRows.size() || column >= instance.mColumnNum) { return false; } @@ -87,7 +89,7 @@ public class CursorWindow_host { } public static int nativeGetType(long windowPtr, int row, int column) { - CursorWindow_host instance = sInstances.get(windowPtr); + CursorWindow_ravenwood instance = sInstances.get(windowPtr); if (row >= instance.mRows.size() || column >= instance.mColumnNum) { return Cursor.FIELD_TYPE_NULL; } @@ -101,7 +103,7 @@ public class CursorWindow_host { } public static String nativeGetString(long windowPtr, int row, int column) { - CursorWindow_host instance = sInstances.get(windowPtr); + CursorWindow_ravenwood instance = sInstances.get(windowPtr); if (row >= instance.mRows.size() || column >= instance.mColumnNum) { return null; } @@ -164,7 +166,7 @@ public class CursorWindow_host { } public static void nativeWriteToParcel(long windowPtr, Parcel parcel) { - CursorWindow_host window = sInstances.get(windowPtr); + CursorWindow_ravenwood window = sInstances.get(windowPtr); parcel.writeString(window.mName); parcel.writeInt(window.mColumnNum); parcel.writeInt(window.mRows.size()); @@ -176,7 +178,7 @@ public class CursorWindow_host { public static long nativeCreateFromParcel(Parcel parcel) { long windowPtr = nativeCreate(null, 0); - CursorWindow_host window = sInstances.get(windowPtr); + CursorWindow_ravenwood window = sInstances.get(windowPtr); window.mName = parcel.readString(); window.mColumnNum = parcel.readInt(); int rowCount = parcel.readInt(); diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java index a37648f7e45d..58e524e741b3 100644 --- a/core/java/android/hardware/camera2/CameraCharacteristics.java +++ b/core/java/android/hardware/camera2/CameraCharacteristics.java @@ -560,7 +560,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * on a particular SessionConfiguration.</p> * * @return List of CameraCharacteristic keys containing characterisitics specific to a session - * configuration. If {@link #INFO_SESSION_CONFIGURATION_QUERY_VERSION} is + * configuration. If {@link #INFO_SESSION_CONFIGURATION_QUERY_VERSION} is at least * {@link Build.VERSION_CODES#VANILLA_ICE_CREAM}, then this list will only contain * CONTROL_ZOOM_RATIO_RANGE and SCALER_AVAILABLE_MAX_DIGITAL_ZOOM * @@ -1436,6 +1436,24 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri new Key<android.util.Range<Float>>("android.control.lowLightBoostInfoLuminanceRange", new TypeReference<android.util.Range<Float>>() {{ }}); /** + * <p>List of auto-exposure priority modes for {@link CaptureRequest#CONTROL_AE_PRIORITY_MODE android.control.aePriorityMode} + * that are supported by this camera device.</p> + * <p>This entry lists the valid modes for + * {@link CaptureRequest#CONTROL_AE_PRIORITY_MODE android.control.aePriorityMode} for this camera device. + * If no AE priority modes are available for a device, this will only list OFF.</p> + * <p><b>Range of valid values:</b><br> + * Any value listed in {@link CaptureRequest#CONTROL_AE_PRIORITY_MODE android.control.aePriorityMode}</p> + * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> + * + * @see CaptureRequest#CONTROL_AE_PRIORITY_MODE + */ + @PublicKey + @NonNull + @FlaggedApi(Flags.FLAG_AE_PRIORITY) + public static final Key<int[]> CONTROL_AE_AVAILABLE_PRIORITY_MODES = + new Key<int[]>("android.control.aeAvailablePriorityModes", int[].class); + + /** * <p>List of edge enhancement modes for {@link CaptureRequest#EDGE_MODE android.edge.mode} that are supported by this camera * device.</p> * <p>Full-capability camera devices must always support OFF; camera devices that support @@ -5241,6 +5259,32 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * <p>All of the above configurations can be set up with a SessionConfiguration. The list of * OutputConfiguration contains the stream configurations and DYNAMIC_RANGE_PROFILE, and * the AE_TARGET_FPS_RANGE and VIDEO_STABILIZATION_MODE are set as session parameters.</p> + * <p>When set to BAKLAVA, the additional stream combinations below are verified + * by the compliance tests:</p> + * <table> + * <thead> + * <tr> + * <th style="text-align: center;">Target 1</th> + * <th style="text-align: center;">Size</th> + * <th style="text-align: center;">Target 2</th> + * <th style="text-align: center;">Size</th> + * </tr> + * </thead> + * <tbody> + * <tr> + * <td style="text-align: center;">PRIV</td> + * <td style="text-align: center;">S1080P</td> + * <td style="text-align: center;">PRIV</td> + * <td style="text-align: center;">S1080P</td> + * </tr> + * <tr> + * <td style="text-align: center;">PRIV</td> + * <td style="text-align: center;">S1080P</td> + * <td style="text-align: center;">PRIV</td> + * <td style="text-align: center;">S1440P</td> + * </tr> + * </tbody> + * </table> * <p>This key is available on all devices.</p> */ @PublicKey diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java index b7856303fc05..75e20582b7b4 100644 --- a/core/java/android/hardware/camera2/CameraManager.java +++ b/core/java/android/hardware/camera2/CameraManager.java @@ -994,7 +994,7 @@ public final class CameraManager { AttributionSourceState contextAttributionSourceState = contextAttributionSource.asState(); - if (Flags.useContextAttributionSource() && useContextAttributionSource) { + if (Flags.dataDeliveryPermissionChecks() && useContextAttributionSource) { return contextAttributionSourceState; } else { AttributionSourceState clientAttribution = diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java index 86bbd4a57a63..8d36fbdc8b10 100644 --- a/core/java/android/hardware/camera2/CameraMetadata.java +++ b/core/java/android/hardware/camera2/CameraMetadata.java @@ -3371,6 +3371,74 @@ public abstract class CameraMetadata<TKey> { public static final int CONTROL_AUTOFRAMING_AUTO = 2; // + // Enumeration values for CaptureRequest#CONTROL_ZOOM_METHOD + // + + /** + * <p>The camera device automatically detects whether the application does zoom with + * {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} or {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio}, and in turn decides which + * metadata tag reflects the effective zoom level.</p> + * + * @see CaptureRequest#CONTROL_ZOOM_RATIO + * @see CaptureRequest#SCALER_CROP_REGION + * @see CaptureRequest#CONTROL_ZOOM_METHOD + */ + @FlaggedApi(Flags.FLAG_ZOOM_METHOD) + public static final int CONTROL_ZOOM_METHOD_AUTO = 0; + + /** + * <p>The application intends to control zoom via {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio}, and + * the effective zoom level is reflected by {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} in capture results.</p> + * + * @see CaptureRequest#CONTROL_ZOOM_RATIO + * @see CaptureRequest#CONTROL_ZOOM_METHOD + */ + @FlaggedApi(Flags.FLAG_ZOOM_METHOD) + public static final int CONTROL_ZOOM_METHOD_ZOOM_RATIO = 1; + + // + // Enumeration values for CaptureRequest#CONTROL_AE_PRIORITY_MODE + // + + /** + * <p>Disable AE priority mode. This is the default value.</p> + * @see CaptureRequest#CONTROL_AE_PRIORITY_MODE + */ + @FlaggedApi(Flags.FLAG_AE_PRIORITY) + public static final int CONTROL_AE_PRIORITY_MODE_OFF = 0; + + /** + * <p>The camera device's auto-exposure routine is active and + * prioritizes the application-selected ISO ({@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity}).</p> + * <p>The application has control over {@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity} while + * the application's values for {@link CaptureRequest#SENSOR_EXPOSURE_TIME android.sensor.exposureTime} and + * {@link CaptureRequest#SENSOR_FRAME_DURATION android.sensor.frameDuration} are ignored.</p> + * + * @see CaptureRequest#SENSOR_EXPOSURE_TIME + * @see CaptureRequest#SENSOR_FRAME_DURATION + * @see CaptureRequest#SENSOR_SENSITIVITY + * @see CaptureRequest#CONTROL_AE_PRIORITY_MODE + */ + @FlaggedApi(Flags.FLAG_AE_PRIORITY) + public static final int CONTROL_AE_PRIORITY_MODE_SENSOR_SENSITIVITY_PRIORITY = 1; + + /** + * <p>The camera device's auto-exposure routine is active and + * prioritizes the application-selected exposure time + * ({@link CaptureRequest#SENSOR_EXPOSURE_TIME android.sensor.exposureTime}).</p> + * <p>The application has control over {@link CaptureRequest#SENSOR_EXPOSURE_TIME android.sensor.exposureTime} while + * the application's values for {@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity} and + * {@link CaptureRequest#SENSOR_FRAME_DURATION android.sensor.frameDuration} are ignored.</p> + * + * @see CaptureRequest#SENSOR_EXPOSURE_TIME + * @see CaptureRequest#SENSOR_FRAME_DURATION + * @see CaptureRequest#SENSOR_SENSITIVITY + * @see CaptureRequest#CONTROL_AE_PRIORITY_MODE + */ + @FlaggedApi(Flags.FLAG_AE_PRIORITY) + public static final int CONTROL_AE_PRIORITY_MODE_SENSOR_EXPOSURE_TIME_PRIORITY = 2; + + // // Enumeration values for CaptureRequest#EDGE_MODE // @@ -4289,6 +4357,39 @@ public abstract class CameraMetadata<TKey> { */ public static final int SYNC_FRAME_NUMBER_UNKNOWN = -2; + // + // Enumeration values for CaptureResult#EXTENSION_NIGHT_MODE_INDICATOR + // + + /** + * <p>The camera can't accurately assess the scene's lighting to determine if a Night Mode + * Camera Extension capture would improve the photo. This can happen when the current + * camera configuration doesn't support night mode indicator detection, such as when + * the auto exposure mode is ON_AUTO_FLASH, ON_ALWAYS_FLASH, ON_AUTO_FLASH_REDEYE, or + * ON_EXTERNAL_FLASH.</p> + * @see CaptureResult#EXTENSION_NIGHT_MODE_INDICATOR + */ + @FlaggedApi(Flags.FLAG_NIGHT_MODE_INDICATOR) + public static final int EXTENSION_NIGHT_MODE_INDICATOR_UNKNOWN = 0; + + /** + * <p>The camera has detected lighting conditions that are sufficiently bright. Night + * Mode Camera Extensions is available but may not be able to optimize the camera + * settings to take a higher quality photo.</p> + * @see CaptureResult#EXTENSION_NIGHT_MODE_INDICATOR + */ + @FlaggedApi(Flags.FLAG_NIGHT_MODE_INDICATOR) + public static final int EXTENSION_NIGHT_MODE_INDICATOR_OFF = 1; + + /** + * <p>The camera has detected low-light conditions. It is recommended to use Night Mode + * Camera Extension to optimize the camera settings to take a high-quality photo in + * the dark.</p> + * @see CaptureResult#EXTENSION_NIGHT_MODE_INDICATOR + */ + @FlaggedApi(Flags.FLAG_NIGHT_MODE_INDICATOR) + public static final int EXTENSION_NIGHT_MODE_INDICATOR_ON = 2; + /*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~ * End generated code *~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~O@*/ diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java index 8142bbe9b838..496d316eb028 100644 --- a/core/java/android/hardware/camera2/CaptureRequest.java +++ b/core/java/android/hardware/camera2/CaptureRequest.java @@ -21,7 +21,6 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import android.hardware.camera2.impl.CameraMetadataNative; -import android.hardware.camera2.impl.ExtensionKey; import android.hardware.camera2.impl.PublicKey; import android.hardware.camera2.impl.SyntheticKey; import android.hardware.camera2.params.OutputConfiguration; @@ -1407,7 +1406,9 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * application's selected exposure time, sensor sensitivity, * and frame duration ({@link CaptureRequest#SENSOR_EXPOSURE_TIME android.sensor.exposureTime}, * {@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity}, and - * {@link CaptureRequest#SENSOR_FRAME_DURATION android.sensor.frameDuration}). If one of the FLASH modes + * {@link CaptureRequest#SENSOR_FRAME_DURATION android.sensor.frameDuration}). If {@link CaptureRequest#CONTROL_AE_PRIORITY_MODE android.control.aePriorityMode} is + * enabled, the relevant priority CaptureRequest settings will not be overridden. + * See {@link CaptureRequest#CONTROL_AE_PRIORITY_MODE android.control.aePriorityMode} for more details. If one of the FLASH modes * is selected, the camera device's flash unit controls are * also overridden.</p> * <p>The FLASH modes are only available if the camera device @@ -1441,6 +1442,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * * @see CameraCharacteristics#CONTROL_AE_AVAILABLE_MODES * @see CaptureRequest#CONTROL_AE_MODE + * @see CaptureRequest#CONTROL_AE_PRIORITY_MODE * @see CaptureRequest#CONTROL_MODE * @see CameraCharacteristics#FLASH_INFO_AVAILABLE * @see CaptureRequest#FLASH_MODE @@ -2668,6 +2670,85 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> new Key<Integer>("android.control.autoframing", int.class); /** + * <p>Whether the application uses {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} or {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} + * to control zoom levels.</p> + * <p>If set to AUTO, the camera device detects which capture request key the application uses + * to do zoom, {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} or {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio}. If + * the application doesn't set android.scaler.zoomRatio or sets it to 1.0 in the capture + * request, the effective zoom level is reflected in {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} in capture + * results. If {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} is set to values other than 1.0, the effective + * zoom level is reflected in {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio}. AUTO is the default value + * for this control, and also the behavior of the OS before Android version + * {@link android.os.Build.VERSION_CODES#BAKLAVA BAKLAVA}.</p> + * <p>If set to ZOOM_RATIO, the application explicitly specifies zoom level be controlled + * by {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio}, and the effective zoom level is reflected in + * {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} in capture results. This addresses an ambiguity with AUTO, + * with which the camera device cannot know if the application is using cropRegion or + * zoomRatio at 1.0x.</p> + * <p><b>Possible values:</b></p> + * <ul> + * <li>{@link #CONTROL_ZOOM_METHOD_AUTO AUTO}</li> + * <li>{@link #CONTROL_ZOOM_METHOD_ZOOM_RATIO ZOOM_RATIO}</li> + * </ul> + * + * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> + * <p><b>Limited capability</b> - + * Present on all camera devices that report being at least {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED HARDWARE_LEVEL_LIMITED} devices in the + * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p> + * + * @see CaptureRequest#CONTROL_ZOOM_RATIO + * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL + * @see CaptureRequest#SCALER_CROP_REGION + * @see #CONTROL_ZOOM_METHOD_AUTO + * @see #CONTROL_ZOOM_METHOD_ZOOM_RATIO + */ + @PublicKey + @NonNull + @FlaggedApi(Flags.FLAG_ZOOM_METHOD) + public static final Key<Integer> CONTROL_ZOOM_METHOD = + new Key<Integer>("android.control.zoomMethod", int.class); + + /** + * <p>Turn on AE priority mode.</p> + * <p>This control is only effective if {@link CaptureRequest#CONTROL_MODE android.control.mode} is + * AUTO and {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} is set to one of its + * ON modes, with the exception of ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY.</p> + * <p>When a priority mode is enabled, the camera device's + * auto-exposure routine will maintain the application's + * selected parameters relevant to the priority mode while overriding + * the remaining exposure parameters + * ({@link CaptureRequest#SENSOR_EXPOSURE_TIME android.sensor.exposureTime}, {@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity}, and + * {@link CaptureRequest#SENSOR_FRAME_DURATION android.sensor.frameDuration}). For example, if + * SENSOR_SENSITIVITY_PRIORITY mode is enabled, the camera device will + * maintain the application-selected {@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity} + * while adjusting {@link CaptureRequest#SENSOR_EXPOSURE_TIME android.sensor.exposureTime} + * and {@link CaptureRequest#SENSOR_FRAME_DURATION android.sensor.frameDuration}. The overridden fields for a + * given capture will be available in its CaptureResult.</p> + * <p><b>Possible values:</b></p> + * <ul> + * <li>{@link #CONTROL_AE_PRIORITY_MODE_OFF OFF}</li> + * <li>{@link #CONTROL_AE_PRIORITY_MODE_SENSOR_SENSITIVITY_PRIORITY SENSOR_SENSITIVITY_PRIORITY}</li> + * <li>{@link #CONTROL_AE_PRIORITY_MODE_SENSOR_EXPOSURE_TIME_PRIORITY SENSOR_EXPOSURE_TIME_PRIORITY}</li> + * </ul> + * + * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> + * + * @see CaptureRequest#CONTROL_AE_MODE + * @see CaptureRequest#CONTROL_MODE + * @see CaptureRequest#SENSOR_EXPOSURE_TIME + * @see CaptureRequest#SENSOR_FRAME_DURATION + * @see CaptureRequest#SENSOR_SENSITIVITY + * @see #CONTROL_AE_PRIORITY_MODE_OFF + * @see #CONTROL_AE_PRIORITY_MODE_SENSOR_SENSITIVITY_PRIORITY + * @see #CONTROL_AE_PRIORITY_MODE_SENSOR_EXPOSURE_TIME_PRIORITY + */ + @PublicKey + @NonNull + @FlaggedApi(Flags.FLAG_AE_PRIORITY) + public static final Key<Integer> CONTROL_AE_PRIORITY_MODE = + new Key<Integer>("android.control.aePriorityMode", int.class); + + /** * <p>Operation mode for edge * enhancement.</p> * <p>Edge enhancement improves sharpness and details in the captured image. OFF means @@ -3489,7 +3570,9 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * duration exposed to the nearest possible value (rather than expose longer). * The final exposure time used will be available in the output capture result.</p> * <p>This control is only effective if {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} or {@link CaptureRequest#CONTROL_MODE android.control.mode} is set to - * OFF; otherwise the auto-exposure algorithm will override this value.</p> + * OFF; otherwise the auto-exposure algorithm will override this value. However, in the + * case that {@link CaptureRequest#CONTROL_AE_PRIORITY_MODE android.control.aePriorityMode} is set to SENSOR_EXPOSURE_TIME_PRIORITY, this + * control will be effective and not controlled by the auto-exposure algorithm.</p> * <p><b>Units</b>: Nanoseconds</p> * <p><b>Range of valid values:</b><br> * {@link CameraCharacteristics#SENSOR_INFO_EXPOSURE_TIME_RANGE android.sensor.info.exposureTimeRange}</p> @@ -3499,6 +3582,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p> * * @see CaptureRequest#CONTROL_AE_MODE + * @see CaptureRequest#CONTROL_AE_PRIORITY_MODE * @see CaptureRequest#CONTROL_MODE * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL * @see CameraCharacteristics#SENSOR_INFO_EXPOSURE_TIME_RANGE @@ -3607,7 +3691,9 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * value. The final sensitivity used will be available in the * output capture result.</p> * <p>This control is only effective if {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} or {@link CaptureRequest#CONTROL_MODE android.control.mode} is set to - * OFF; otherwise the auto-exposure algorithm will override this value.</p> + * OFF; otherwise the auto-exposure algorithm will override this value. However, in the + * case that {@link CaptureRequest#CONTROL_AE_PRIORITY_MODE android.control.aePriorityMode} is set to SENSOR_SENSITIVITY_PRIORITY, this + * control will be effective and not controlled by the auto-exposure algorithm.</p> * <p>Note that for devices supporting postRawSensitivityBoost, the total sensitivity applied * to the final processed image is the combination of {@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity} and * {@link CaptureRequest#CONTROL_POST_RAW_SENSITIVITY_BOOST android.control.postRawSensitivityBoost}. In case the application uses the sensor @@ -3623,6 +3709,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p> * * @see CaptureRequest#CONTROL_AE_MODE + * @see CaptureRequest#CONTROL_AE_PRIORITY_MODE * @see CaptureRequest#CONTROL_MODE * @see CaptureRequest#CONTROL_POST_RAW_SENSITIVITY_BOOST * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java index ae72ca40fc5a..a52be973f564 100644 --- a/core/java/android/hardware/camera2/CaptureResult.java +++ b/core/java/android/hardware/camera2/CaptureResult.java @@ -22,7 +22,6 @@ import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import android.hardware.camera2.impl.CameraMetadataNative; import android.hardware.camera2.impl.CaptureResultExtras; -import android.hardware.camera2.impl.ExtensionKey; import android.hardware.camera2.impl.PublicKey; import android.hardware.camera2.impl.SyntheticKey; import android.hardware.camera2.utils.TypeReference; @@ -808,7 +807,9 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * application's selected exposure time, sensor sensitivity, * and frame duration ({@link CaptureRequest#SENSOR_EXPOSURE_TIME android.sensor.exposureTime}, * {@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity}, and - * {@link CaptureRequest#SENSOR_FRAME_DURATION android.sensor.frameDuration}). If one of the FLASH modes + * {@link CaptureRequest#SENSOR_FRAME_DURATION android.sensor.frameDuration}). If {@link CaptureRequest#CONTROL_AE_PRIORITY_MODE android.control.aePriorityMode} is + * enabled, the relevant priority CaptureRequest settings will not be overridden. + * See {@link CaptureRequest#CONTROL_AE_PRIORITY_MODE android.control.aePriorityMode} for more details. If one of the FLASH modes * is selected, the camera device's flash unit controls are * also overridden.</p> * <p>The FLASH modes are only available if the camera device @@ -842,6 +843,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * * @see CameraCharacteristics#CONTROL_AE_AVAILABLE_MODES * @see CaptureRequest#CONTROL_AE_MODE + * @see CaptureRequest#CONTROL_AE_PRIORITY_MODE * @see CaptureRequest#CONTROL_MODE * @see CameraCharacteristics#FLASH_INFO_AVAILABLE * @see CaptureRequest#FLASH_MODE @@ -2915,6 +2917,85 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { new Key<Integer>("android.control.lowLightBoostState", int.class); /** + * <p>Whether the application uses {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} or {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} + * to control zoom levels.</p> + * <p>If set to AUTO, the camera device detects which capture request key the application uses + * to do zoom, {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} or {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio}. If + * the application doesn't set android.scaler.zoomRatio or sets it to 1.0 in the capture + * request, the effective zoom level is reflected in {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} in capture + * results. If {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} is set to values other than 1.0, the effective + * zoom level is reflected in {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio}. AUTO is the default value + * for this control, and also the behavior of the OS before Android version + * {@link android.os.Build.VERSION_CODES#BAKLAVA BAKLAVA}.</p> + * <p>If set to ZOOM_RATIO, the application explicitly specifies zoom level be controlled + * by {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio}, and the effective zoom level is reflected in + * {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} in capture results. This addresses an ambiguity with AUTO, + * with which the camera device cannot know if the application is using cropRegion or + * zoomRatio at 1.0x.</p> + * <p><b>Possible values:</b></p> + * <ul> + * <li>{@link #CONTROL_ZOOM_METHOD_AUTO AUTO}</li> + * <li>{@link #CONTROL_ZOOM_METHOD_ZOOM_RATIO ZOOM_RATIO}</li> + * </ul> + * + * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> + * <p><b>Limited capability</b> - + * Present on all camera devices that report being at least {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED HARDWARE_LEVEL_LIMITED} devices in the + * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p> + * + * @see CaptureRequest#CONTROL_ZOOM_RATIO + * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL + * @see CaptureRequest#SCALER_CROP_REGION + * @see #CONTROL_ZOOM_METHOD_AUTO + * @see #CONTROL_ZOOM_METHOD_ZOOM_RATIO + */ + @PublicKey + @NonNull + @FlaggedApi(Flags.FLAG_ZOOM_METHOD) + public static final Key<Integer> CONTROL_ZOOM_METHOD = + new Key<Integer>("android.control.zoomMethod", int.class); + + /** + * <p>Turn on AE priority mode.</p> + * <p>This control is only effective if {@link CaptureRequest#CONTROL_MODE android.control.mode} is + * AUTO and {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} is set to one of its + * ON modes, with the exception of ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY.</p> + * <p>When a priority mode is enabled, the camera device's + * auto-exposure routine will maintain the application's + * selected parameters relevant to the priority mode while overriding + * the remaining exposure parameters + * ({@link CaptureRequest#SENSOR_EXPOSURE_TIME android.sensor.exposureTime}, {@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity}, and + * {@link CaptureRequest#SENSOR_FRAME_DURATION android.sensor.frameDuration}). For example, if + * SENSOR_SENSITIVITY_PRIORITY mode is enabled, the camera device will + * maintain the application-selected {@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity} + * while adjusting {@link CaptureRequest#SENSOR_EXPOSURE_TIME android.sensor.exposureTime} + * and {@link CaptureRequest#SENSOR_FRAME_DURATION android.sensor.frameDuration}. The overridden fields for a + * given capture will be available in its CaptureResult.</p> + * <p><b>Possible values:</b></p> + * <ul> + * <li>{@link #CONTROL_AE_PRIORITY_MODE_OFF OFF}</li> + * <li>{@link #CONTROL_AE_PRIORITY_MODE_SENSOR_SENSITIVITY_PRIORITY SENSOR_SENSITIVITY_PRIORITY}</li> + * <li>{@link #CONTROL_AE_PRIORITY_MODE_SENSOR_EXPOSURE_TIME_PRIORITY SENSOR_EXPOSURE_TIME_PRIORITY}</li> + * </ul> + * + * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> + * + * @see CaptureRequest#CONTROL_AE_MODE + * @see CaptureRequest#CONTROL_MODE + * @see CaptureRequest#SENSOR_EXPOSURE_TIME + * @see CaptureRequest#SENSOR_FRAME_DURATION + * @see CaptureRequest#SENSOR_SENSITIVITY + * @see #CONTROL_AE_PRIORITY_MODE_OFF + * @see #CONTROL_AE_PRIORITY_MODE_SENSOR_SENSITIVITY_PRIORITY + * @see #CONTROL_AE_PRIORITY_MODE_SENSOR_EXPOSURE_TIME_PRIORITY + */ + @PublicKey + @NonNull + @FlaggedApi(Flags.FLAG_AE_PRIORITY) + public static final Key<Integer> CONTROL_AE_PRIORITY_MODE = + new Key<Integer>("android.control.aePriorityMode", int.class); + + /** * <p>Operation mode for edge * enhancement.</p> * <p>Edge enhancement improves sharpness and details in the captured image. OFF means @@ -4199,7 +4280,9 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * duration exposed to the nearest possible value (rather than expose longer). * The final exposure time used will be available in the output capture result.</p> * <p>This control is only effective if {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} or {@link CaptureRequest#CONTROL_MODE android.control.mode} is set to - * OFF; otherwise the auto-exposure algorithm will override this value.</p> + * OFF; otherwise the auto-exposure algorithm will override this value. However, in the + * case that {@link CaptureRequest#CONTROL_AE_PRIORITY_MODE android.control.aePriorityMode} is set to SENSOR_EXPOSURE_TIME_PRIORITY, this + * control will be effective and not controlled by the auto-exposure algorithm.</p> * <p><b>Units</b>: Nanoseconds</p> * <p><b>Range of valid values:</b><br> * {@link CameraCharacteristics#SENSOR_INFO_EXPOSURE_TIME_RANGE android.sensor.info.exposureTimeRange}</p> @@ -4209,6 +4292,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p> * * @see CaptureRequest#CONTROL_AE_MODE + * @see CaptureRequest#CONTROL_AE_PRIORITY_MODE * @see CaptureRequest#CONTROL_MODE * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL * @see CameraCharacteristics#SENSOR_INFO_EXPOSURE_TIME_RANGE @@ -4317,7 +4401,9 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * value. The final sensitivity used will be available in the * output capture result.</p> * <p>This control is only effective if {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} or {@link CaptureRequest#CONTROL_MODE android.control.mode} is set to - * OFF; otherwise the auto-exposure algorithm will override this value.</p> + * OFF; otherwise the auto-exposure algorithm will override this value. However, in the + * case that {@link CaptureRequest#CONTROL_AE_PRIORITY_MODE android.control.aePriorityMode} is set to SENSOR_SENSITIVITY_PRIORITY, this + * control will be effective and not controlled by the auto-exposure algorithm.</p> * <p>Note that for devices supporting postRawSensitivityBoost, the total sensitivity applied * to the final processed image is the combination of {@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity} and * {@link CaptureRequest#CONTROL_POST_RAW_SENSITIVITY_BOOST android.control.postRawSensitivityBoost}. In case the application uses the sensor @@ -4333,6 +4419,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p> * * @see CaptureRequest#CONTROL_AE_MODE + * @see CaptureRequest#CONTROL_AE_PRIORITY_MODE * @see CaptureRequest#CONTROL_MODE * @see CaptureRequest#CONTROL_POST_RAW_SENSITIVITY_BOOST * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL @@ -6016,6 +6103,38 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { public static final Key<Integer> EXTENSION_STRENGTH = new Key<Integer>("android.extension.strength", int.class); + /** + * <p>Indicates when to activate Night Mode Camera Extension for high-quality + * still captures in low-light conditions.</p> + * <p>Provides awareness to the application when the current scene can benefit from using a + * Night Mode Camera Extension to take a high-quality photo.</p> + * <p>Support for this capture result can be queried via + * {@link android.hardware.camera2.CameraCharacteristics#getAvailableCaptureResultKeys }.</p> + * <p>If the device supports this capability then it will also support + * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_NIGHT NIGHT} + * and will be available in both + * {@link android.hardware.camera2.CameraCaptureSession sessions} and + * {@link android.hardware.camera2.CameraExtensionSession sessions}.</p> + * <p>The value will be {@code UNKNOWN} in the following auto exposure modes: ON_AUTO_FLASH, + * ON_ALWAYS_FLASH, ON_AUTO_FLASH_REDEYE, or ON_EXTERNAL_FLASH.</p> + * <p><b>Possible values:</b></p> + * <ul> + * <li>{@link #EXTENSION_NIGHT_MODE_INDICATOR_UNKNOWN UNKNOWN}</li> + * <li>{@link #EXTENSION_NIGHT_MODE_INDICATOR_OFF OFF}</li> + * <li>{@link #EXTENSION_NIGHT_MODE_INDICATOR_ON ON}</li> + * </ul> + * + * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> + * @see #EXTENSION_NIGHT_MODE_INDICATOR_UNKNOWN + * @see #EXTENSION_NIGHT_MODE_INDICATOR_OFF + * @see #EXTENSION_NIGHT_MODE_INDICATOR_ON + */ + @PublicKey + @NonNull + @FlaggedApi(Flags.FLAG_NIGHT_MODE_INDICATOR) + public static final Key<Integer> EXTENSION_NIGHT_MODE_INDICATOR = + new Key<Integer>("android.extension.nightModeIndicator", int.class); + /*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~ * End generated code *~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~O@*/ diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java index e22c263e893d..1cc085658bfa 100644 --- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java +++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java @@ -437,7 +437,7 @@ public class CameraMetadataNative implements Parcelable { } @Override - public void writeToParcel(Parcel dest, int flags) { + public synchronized void writeToParcel(Parcel dest, int flags) { nativeWriteToParcel(dest, mMetadataPtr); } @@ -479,7 +479,7 @@ public class CameraMetadataNative implements Parcelable { return getBase(key); } - public void readFromParcel(Parcel in) { + public synchronized void readFromParcel(Parcel in) { nativeReadFromParcel(in, mMetadataPtr); updateNativeAllocation(); } @@ -592,28 +592,33 @@ public class CameraMetadataNative implements Parcelable { } private <T> T getBase(Key<T> key) { - int tag; - if (key.hasTag()) { - tag = key.getTag(); - } else { - tag = nativeGetTagFromKeyLocal(mMetadataPtr, key.getName()); - key.cacheTag(tag); - } - byte[] values = readValues(tag); - if (values == null) { - // If the key returns null, use the fallback key if exists. - // This is to support old key names for the newly published keys. - if (key.mFallbackName == null) { - return null; + int tag, nativeType; + byte[] values = null; + synchronized (this) { + if (key.hasTag()) { + tag = key.getTag(); + } else { + tag = nativeGetTagFromKeyLocal(mMetadataPtr, key.getName()); + key.cacheTag(tag); } - tag = nativeGetTagFromKeyLocal(mMetadataPtr, key.mFallbackName); values = readValues(tag); if (values == null) { - return null; + // If the key returns null, use the fallback key if exists. + // This is to support old key names for the newly published keys. + if (key.mFallbackName == null) { + return null; + } + tag = nativeGetTagFromKeyLocal(mMetadataPtr, key.mFallbackName); + values = readValues(tag); + if (values == null) { + return null; + } } - } - int nativeType = nativeGetTypeFromTagLocal(mMetadataPtr, tag); + nativeType = nativeGetTypeFromTagLocal(mMetadataPtr, tag); + } + // This block of code doesn't need to be synchronized since we aren't writing or reading + // from the metadata buffer for this instance of CameraMetadataNative. Marshaler<T> marshaler = getMarshalerForKey(key, nativeType); ByteBuffer buffer = ByteBuffer.wrap(values).order(ByteOrder.nativeOrder()); return marshaler.unmarshal(buffer); @@ -1945,8 +1950,12 @@ public class CameraMetadataNative implements Parcelable { setBase(key.getNativeKey(), value); } - private <T> void setBase(Key<T> key, T value) { - int tag; + // The whole method needs to be synchronized since we're making + // multiple calls to the native layer. From one call to the other (within setBase) + // we expect the metadata's properties such as vendor id etc to + // stay the same and as a result the whole method should be synchronized for safety. + private synchronized <T> void setBase(Key<T> key, T value) { + int tag, nativeType; if (key.hasTag()) { tag = key.getTag(); } else { @@ -1959,7 +1968,7 @@ public class CameraMetadataNative implements Parcelable { return; } // else update the entry to a new value - int nativeType = nativeGetTypeFromTagLocal(mMetadataPtr, tag); + nativeType = nativeGetTypeFromTagLocal(mMetadataPtr, tag); Marshaler<T> marshaler = getMarshalerForKey(key, nativeType); int size = marshaler.calculateMarshalSize(value); @@ -2162,7 +2171,7 @@ public class CameraMetadataNative implements Parcelable { return true; } - private void updateNativeAllocation() { + private synchronized void updateNativeAllocation() { long currentBufferSize = nativeGetBufferSize(mMetadataPtr); if (currentBufferSize != mBufferSize) { @@ -2245,6 +2254,11 @@ public class CameraMetadataNative implements Parcelable { @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) private long mMetadataPtr; // native std::shared_ptr<CameraMetadata>* + // FastNative doesn't work with synchronized methods and we can do synchronization + // wherever needed in the java layer (caller). At some places in java such as + // setBase() / getBase(), we do need to synchronize the whole method, so leaving + // synchronized out for these native methods. + @FastNative private static native long nativeAllocate(); @FastNative @@ -2254,28 +2268,41 @@ public class CameraMetadataNative implements Parcelable { @FastNative private static native void nativeUpdate(long dst, long src); - private static synchronized native void nativeWriteToParcel(Parcel dest, long ptr); - private static synchronized native void nativeReadFromParcel(Parcel source, long ptr); - private static synchronized native void nativeSwap(long ptr, long otherPtr) + @FastNative + private static native void nativeWriteToParcel(Parcel dest, long ptr); + @FastNative + private static native void nativeReadFromParcel(Parcel source, long ptr); + @FastNative + private static native void nativeSwap(long ptr, long otherPtr) throws NullPointerException; @FastNative private static native void nativeSetVendorId(long ptr, long vendorId); - private static synchronized native void nativeClose(long ptr); - private static synchronized native boolean nativeIsEmpty(long ptr); - private static synchronized native int nativeGetEntryCount(long ptr); - private static synchronized native long nativeGetBufferSize(long ptr); + @FastNative + private static native void nativeClose(long ptr); + @FastNative + private static native boolean nativeIsEmpty(long ptr); + @FastNative + private static native int nativeGetEntryCount(long ptr); + @FastNative + private static native long nativeGetBufferSize(long ptr); @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - private static synchronized native byte[] nativeReadValues(int tag, long ptr); - private static synchronized native void nativeWriteValues(int tag, byte[] src, long ptr); - private static synchronized native void nativeDump(long ptr) throws IOException; // dump to LOGD + @FastNative + private static native byte[] nativeReadValues(int tag, long ptr); + @FastNative + private static native void nativeWriteValues(int tag, byte[] src, long ptr); + @FastNative + private static native void nativeDump(long ptr) throws IOException; // dump to LOGD - private static synchronized native ArrayList nativeGetAllVendorKeys(long ptr, Class keyClass); + @FastNative + private static native ArrayList nativeGetAllVendorKeys(long ptr, Class keyClass); @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - private static synchronized native int nativeGetTagFromKeyLocal(long ptr, String keyName) + @FastNative + private static native int nativeGetTagFromKeyLocal(long ptr, String keyName) throws IllegalArgumentException; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - private static synchronized native int nativeGetTypeFromTagLocal(long ptr, int tag) + @FastNative + private static native int nativeGetTypeFromTagLocal(long ptr, int tag) throws IllegalArgumentException; @FastNative private static native int nativeGetTagFromKey(String keyName, long vendorId) @@ -2293,7 +2320,7 @@ public class CameraMetadataNative implements Parcelable { * @throws NullPointerException if other was null * @hide */ - public void swap(CameraMetadataNative other) { + public synchronized void swap(CameraMetadataNative other) { nativeSwap(mMetadataPtr, other.mMetadataPtr); mCameraId = other.mCameraId; mHasMandatoryConcurrentStreams = other.mHasMandatoryConcurrentStreams; @@ -2308,14 +2335,14 @@ public class CameraMetadataNative implements Parcelable { * * @hide */ - public void setVendorId(long vendorId) { + public synchronized void setVendorId(long vendorId) { nativeSetVendorId(mMetadataPtr, vendorId); } /** * @hide */ - public int getEntryCount() { + public synchronized int getEntryCount() { return nativeGetEntryCount(mMetadataPtr); } @@ -2324,7 +2351,7 @@ public class CameraMetadataNative implements Parcelable { * * @hide */ - public boolean isEmpty() { + public synchronized boolean isEmpty() { return nativeIsEmpty(mMetadataPtr); } @@ -2343,7 +2370,7 @@ public class CameraMetadataNative implements Parcelable { * * @hide */ - public <K> ArrayList<K> getAllVendorKeys(Class<K> keyClass) { + public synchronized <K> ArrayList<K> getAllVendorKeys(Class<K> keyClass) { if (keyClass == null) { throw new NullPointerException(); } @@ -2398,7 +2425,7 @@ public class CameraMetadataNative implements Parcelable { * * @hide */ - public void writeValues(int tag, byte[] src) { + public synchronized void writeValues(int tag, byte[] src) { nativeWriteValues(tag, src, mMetadataPtr); } @@ -2413,7 +2440,7 @@ public class CameraMetadataNative implements Parcelable { * @return {@code null} if there were 0 entries for this tag, a byte[] otherwise. * @hide */ - public byte[] readValues(int tag) { + public synchronized byte[] readValues(int tag) { // TODO: Optimization. Native code returns a ByteBuffer instead. return nativeReadValues(tag, mMetadataPtr); } @@ -2426,7 +2453,7 @@ public class CameraMetadataNative implements Parcelable { * * @hide */ - public void dumpToLog() { + public synchronized void dumpToLog() { try { nativeDump(mMetadataPtr); } catch (IOException e) { diff --git a/core/java/android/hardware/devicestate/DeviceState.java b/core/java/android/hardware/devicestate/DeviceState.java index e583627c0960..8b4d0da147bc 100644 --- a/core/java/android/hardware/devicestate/DeviceState.java +++ b/core/java/android/hardware/devicestate/DeviceState.java @@ -172,6 +172,23 @@ public final class DeviceState { */ public static final int PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT = 17; + /** + * Property that indicates that this state corresponds to the device state for rear display + * mode, where both the inner and outer displays are on. In this state, the outer display + * is the default display where the app is shown, and the inner display is used by the system to + * show a UI affordance for exiting the mode. + * + * Note that this value should generally not be used, and may be removed in the future (e.g. + * if or when it becomes the only type of rear display mode when + * {@link android.hardware.devicestate.feature.flags.Flags#deviceStateRdmV2} is removed). + * + * As such, clients should strongly consider relying on {@link #PROPERTY_FEATURE_REAR_DISPLAY} + * instead. + * + * @hide + */ + public static final int PROPERTY_FEATURE_REAR_DISPLAY_OUTER_DEFAULT = 1001; + /** @hide */ @IntDef(prefix = {"PROPERTY_"}, flag = false, value = { PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED, @@ -190,7 +207,8 @@ public final class DeviceState { PROPERTY_POWER_CONFIGURATION_TRIGGER_WAKE, PROPERTY_EXTENDED_DEVICE_STATE_EXTERNAL_DISPLAY, PROPERTY_FEATURE_REAR_DISPLAY, - PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT + PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT, + PROPERTY_FEATURE_REAR_DISPLAY_OUTER_DEFAULT }) @Retention(RetentionPolicy.SOURCE) @Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE}) diff --git a/core/java/android/hardware/devicestate/feature/flags.aconfig b/core/java/android/hardware/devicestate/feature/flags.aconfig index 98ba9192044d..6230f4dbf6f4 100644 --- a/core/java/android/hardware/devicestate/feature/flags.aconfig +++ b/core/java/android/hardware/devicestate/feature/flags.aconfig @@ -29,4 +29,13 @@ flag { metadata { purpose: PURPOSE_BUGFIX } +} + +flag { + name: "device_state_rdm_v2" + is_exported: true + namespace: "windowing_sdk" + description: "Enables Rear Display Mode V2, where the inner display shows the user a UI affordance for exiting the state" + bug: "372486634" + is_fixed_read_only: true }
\ No newline at end of file diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java index a452226c81ac..28da644dd837 100644 --- a/core/java/android/hardware/display/DisplayManager.java +++ b/core/java/android/hardware/display/DisplayManager.java @@ -16,6 +16,7 @@ package android.hardware.display; +import static android.Manifest.permission.MANAGE_DISPLAYS; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.HdrCapabilities.HdrType; import static android.view.Display.INVALID_DISPLAY; @@ -1764,6 +1765,29 @@ public final class DisplayManager { } /** + * @return The current display topology that represents the relative positions of extended + * displays. + * + * @hide + */ + @RequiresPermission(MANAGE_DISPLAYS) + @Nullable + public DisplayTopology getDisplayTopology() { + return mGlobal.getDisplayTopology(); + } + + /** + * Set the relative positions between extended displays (display topology). + * @param topology The display topology to be set + * + * @hide + */ + @RequiresPermission(MANAGE_DISPLAYS) + public void setDisplayTopology(DisplayTopology topology) { + mGlobal.setDisplayTopology(topology); + } + + /** * Listens for changes in available display devices. */ public interface DisplayListener { diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java index 644850a5c2e1..03b44f63e3b7 100644 --- a/core/java/android/hardware/display/DisplayManagerGlobal.java +++ b/core/java/android/hardware/display/DisplayManagerGlobal.java @@ -18,6 +18,7 @@ package android.hardware.display; import static android.hardware.display.DisplayManager.EventFlag; +import static android.Manifest.permission.MANAGE_DISPLAYS; import static android.view.Display.HdrCapabilities.HdrType; import android.Manifest; @@ -1285,6 +1286,31 @@ public final class DisplayManagerGlobal { } } + /** + * @see DisplayManager#getDisplayTopology + */ + @RequiresPermission(MANAGE_DISPLAYS) + @Nullable + public DisplayTopology getDisplayTopology() { + try { + return mDm.getDisplayTopology(); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } + } + + /** + * @see DisplayManager#setDisplayTopology + */ + @RequiresPermission(MANAGE_DISPLAYS) + public void setDisplayTopology(DisplayTopology topology) { + try { + mDm.setDisplayTopology(topology); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); + } + } + private final class DisplayManagerCallback extends IDisplayManagerCallback.Stub { @Override public void onDisplayEvent(int displayId, @DisplayEvent int event) { diff --git a/core/java/android/hardware/display/DisplayTopology.aidl b/core/java/android/hardware/display/DisplayTopology.aidl new file mode 100644 index 000000000000..e69b777a30de --- /dev/null +++ b/core/java/android/hardware/display/DisplayTopology.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.display; + +parcelable DisplayTopology; diff --git a/services/core/java/com/android/server/display/DisplayTopology.java b/core/java/android/hardware/display/DisplayTopology.java index fdadafeb98c9..e349b81614bc 100644 --- a/services/core/java/com/android/server/display/DisplayTopology.java +++ b/core/java/android/hardware/display/DisplayTopology.java @@ -14,25 +14,34 @@ * limitations under the License. */ -package com.android.server.display; +package android.hardware.display; -import static com.android.server.display.DisplayTopology.TreeNode.Position.POSITION_BOTTOM; -import static com.android.server.display.DisplayTopology.TreeNode.Position.POSITION_LEFT; -import static com.android.server.display.DisplayTopology.TreeNode.Position.POSITION_TOP; -import static com.android.server.display.DisplayTopology.TreeNode.Position.POSITION_RIGHT; +import static android.hardware.display.DisplayTopology.TreeNode.POSITION_BOTTOM; +import static android.hardware.display.DisplayTopology.TreeNode.POSITION_LEFT; +import static android.hardware.display.DisplayTopology.TreeNode.POSITION_RIGHT; +import static android.hardware.display.DisplayTopology.TreeNode.POSITION_TOP; +import android.annotation.IntDef; import android.annotation.Nullable; import android.graphics.RectF; +import android.os.Parcel; +import android.os.Parcelable; import android.util.IndentingPrintWriter; import android.util.Pair; import android.util.Slog; import android.view.Display; +import androidx.annotation.NonNull; + import com.android.internal.annotations.VisibleForTesting; import java.io.PrintWriter; +import java.io.StringWriter; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayDeque; import java.util.ArrayList; +import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; @@ -42,24 +51,59 @@ import java.util.Queue; /** * Represents the relative placement of extended displays. * Does not support concurrent calls, so a lock should be held when calling into this class. + * + * @hide */ -class DisplayTopology { +public final class DisplayTopology implements Parcelable { private static final String TAG = "DisplayTopology"; private static final float EPSILON = 0.0001f; + @android.annotation.NonNull + public static final Creator<DisplayTopology> CREATOR = + new Creator<>() { + @Override + public DisplayTopology createFromParcel(Parcel source) { + return new DisplayTopology(source); + } + + @Override + public DisplayTopology[] newArray(int size) { + return new DisplayTopology[size]; + } + }; + /** * The topology tree */ @Nullable - @VisibleForTesting - TreeNode mRoot; + private TreeNode mRoot; /** * The logical display ID of the primary display that will show certain UI elements. * This is not necessarily the same as the default display. */ + private int mPrimaryDisplayId = Display.INVALID_DISPLAY; + + public DisplayTopology() {} + @VisibleForTesting - int mPrimaryDisplayId = Display.INVALID_DISPLAY; + public DisplayTopology(TreeNode root, int primaryDisplayId) { + mRoot = root; + mPrimaryDisplayId = primaryDisplayId; + } + + public DisplayTopology(Parcel source) { + this(source.readTypedObject(TreeNode.CREATOR), source.readInt()); + } + + @Nullable + public TreeNode getRoot() { + return mRoot; + } + + public int getPrimaryDisplayId() { + return mPrimaryDisplayId; + } /** * Add a display to the topology. @@ -69,7 +113,7 @@ class DisplayTopology { * @param width The width of the display * @param height The height of the display */ - void addDisplay(int displayId, float width, float height) { + public void addDisplay(int displayId, float width, float height) { addDisplay(displayId, width, height, /* shouldLog= */ true); } @@ -79,7 +123,7 @@ class DisplayTopology { * one by one. * @param displayId The logical display ID */ - void removeDisplay(int displayId) { + public void removeDisplay(int displayId) { if (findDisplay(displayId, mRoot) == null) { return; } @@ -106,11 +150,22 @@ class DisplayTopology { } } + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeTypedObject(mRoot, flags); + dest.writeInt(mPrimaryDisplayId); + } + /** * Print the object's state and debug information into the given stream. * @param pw The stream to dump information to. */ - void dump(PrintWriter pw) { + public void dump(PrintWriter pw) { pw.println("DisplayTopology:"); pw.println("--------------------"); IndentingPrintWriter ipw = new IndentingPrintWriter(pw); @@ -126,13 +181,21 @@ class DisplayTopology { } } + @Override + public String toString() { + StringWriter out = new StringWriter(); + PrintWriter writer = new PrintWriter(out); + dump(writer); + return out.toString(); + } + private void addDisplay(int displayId, float width, float height, boolean shouldLog) { if (findDisplay(displayId, mRoot) != null) { throw new IllegalArgumentException( "DisplayTopology: attempting to add a display that already exists"); } if (mRoot == null) { - mRoot = new TreeNode(displayId, width, height, /* position= */ null, /* offset= */ 0); + mRoot = new TreeNode(displayId, width, height, /* position= */ 0, /* offset= */ 0); mPrimaryDisplayId = displayId; if (shouldLog) { Slog.i(TAG, "First display added: " + mRoot); @@ -241,7 +304,7 @@ class DisplayTopology { * Update the topology to remove any overlaps between displays. */ @VisibleForTesting - void normalize() { + public void normalize() { if (mRoot == null) { return; } @@ -341,6 +404,8 @@ class DisplayTopology { case POSITION_RIGHT -> floatEquals(parentBounds.right, childBounds.left); case POSITION_TOP -> floatEquals(parentBounds.top, childBounds.bottom); case POSITION_BOTTOM -> floatEquals(parentBounds.bottom, childBounds.top); + default -> throw new IllegalStateException( + "Unexpected value: " + targetDisplay.mPosition); }; // Check that the offset is within bounds areTouching &= switch (targetDisplay.mPosition) { @@ -350,6 +415,8 @@ class DisplayTopology { case POSITION_TOP, POSITION_BOTTOM -> childBounds.right + EPSILON >= parentBounds.left && childBounds.left <= parentBounds.right + EPSILON; + default -> throw new IllegalStateException( + "Unexpected value: " + targetDisplay.mPosition); }; if (!areTouching) { @@ -379,36 +446,56 @@ class DisplayTopology { * @param b second float to compare * @return whether the two values are within a small enough tolerance value */ - public static boolean floatEquals(float a, float b) { - return a == b || Float.isNaN(a) && Float.isNaN(b) || Math.abs(a - b) < EPSILON; + private static boolean floatEquals(float a, float b) { + return a == b || (Float.isNaN(a) && Float.isNaN(b)) || Math.abs(a - b) < EPSILON; } - @VisibleForTesting - static class TreeNode { + public static final class TreeNode implements Parcelable { + public static final int POSITION_LEFT = 0; + public static final int POSITION_TOP = 1; + public static final int POSITION_RIGHT = 2; + public static final int POSITION_BOTTOM = 3; + + @IntDef(prefix = { "POSITION_" }, value = { + POSITION_LEFT, POSITION_TOP, POSITION_RIGHT, POSITION_BOTTOM + }) + @Retention(RetentionPolicy.SOURCE) + public @interface Position{} + + @android.annotation.NonNull + public static final Creator<TreeNode> CREATOR = + new Creator<>() { + @Override + public TreeNode createFromParcel(Parcel source) { + return new TreeNode(source); + } + + @Override + public TreeNode[] newArray(int size) { + return new TreeNode[size]; + } + }; /** * The logical display ID */ - @VisibleForTesting - final int mDisplayId; + private final int mDisplayId; /** * The width of the display in density-independent pixels (dp). */ - @VisibleForTesting - float mWidth; + private final float mWidth; /** * The height of the display in density-independent pixels (dp). */ - @VisibleForTesting - float mHeight; + private final float mHeight; /** * The position of this display relative to its parent. */ - @VisibleForTesting - Position mPosition; + @Position + private int mPosition; /** * The distance from the top edge of the parent display to the top edge of this display (in @@ -416,13 +503,13 @@ class DisplayTopology { * to the left edge of this display (in case of POSITION_TOP or POSITION_BOTTOM). The unit * used is density-independent pixels (dp). */ - @VisibleForTesting - float mOffset; + private float mOffset; - @VisibleForTesting - final List<TreeNode> mChildren = new ArrayList<>(); + private final List<TreeNode> mChildren = new ArrayList<>(); - TreeNode(int displayId, float width, float height, Position position, float offset) { + @VisibleForTesting + public TreeNode(int displayId, float width, float height, @Position int position, + float offset) { mDisplayId = displayId; mWidth = width; mHeight = height; @@ -430,11 +517,76 @@ class DisplayTopology { mOffset = offset; } + public TreeNode(Parcel source) { + this(source.readInt(), source.readFloat(), source.readFloat(), source.readInt(), + source.readFloat()); + source.readTypedList(mChildren, CREATOR); + } + + public int getDisplayId() { + return mDisplayId; + } + + public float getWidth() { + return mWidth; + } + + public float getHeight() { + return mHeight; + } + + public int getPosition() { + return mPosition; + } + + public float getOffset() { + return mOffset; + } + + public List<TreeNode> getChildren() { + return Collections.unmodifiableList(mChildren); + } + + @Override + public String toString() { + return "Display {id=" + mDisplayId + ", width=" + mWidth + ", height=" + mHeight + + ", position=" + positionToString(mPosition) + ", offset=" + mOffset + "}"; + } + + /** + * @param position The position + * @return The string representation + */ + public static String positionToString(@Position int position) { + return switch (position) { + case POSITION_LEFT -> "left"; + case POSITION_TOP -> "top"; + case POSITION_RIGHT -> "right"; + case POSITION_BOTTOM -> "bottom"; + default -> throw new IllegalStateException("Unexpected value: " + position); + }; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mDisplayId); + dest.writeFloat(mWidth); + dest.writeFloat(mHeight); + dest.writeInt(mPosition); + dest.writeFloat(mOffset); + dest.writeTypedList(mChildren); + } + /** * Print the object's state and debug information into the given stream. * @param ipw The stream to dump information to. */ - void dump(IndentingPrintWriter ipw) { + public void dump(IndentingPrintWriter ipw) { ipw.println(this); ipw.increaseIndent(); for (TreeNode child : mChildren) { @@ -443,15 +595,12 @@ class DisplayTopology { ipw.decreaseIndent(); } - @Override - public String toString() { - return "Display {id=" + mDisplayId + ", width=" + mWidth + ", height=" + mHeight - + ", position=" + mPosition + ", offset=" + mOffset + "}"; - } - + /** + * @param child The child to add + */ @VisibleForTesting - enum Position { - POSITION_LEFT, POSITION_TOP, POSITION_RIGHT, POSITION_BOTTOM + public void addChild(TreeNode child) { + mChildren.add(child); } } } diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl index b612bca5671e..4fbdf7f5afc8 100644 --- a/core/java/android/hardware/display/IDisplayManager.aidl +++ b/core/java/android/hardware/display/IDisplayManager.aidl @@ -23,6 +23,7 @@ import android.hardware.display.BrightnessConfiguration; import android.hardware.display.BrightnessInfo; import android.hardware.display.Curve; import android.hardware.graphics.common.DisplayDecorationSupport; +import android.hardware.display.DisplayTopology; import android.hardware.display.HdrConversionMode; import android.hardware.display.IDisplayManagerCallback; import android.hardware.display.IVirtualDisplayCallback; @@ -254,4 +255,13 @@ interface IDisplayManager { // Get the default doze brightness @EnforcePermission("CONTROL_DISPLAY_BRIGHTNESS") float getDefaultDozeBrightness(int displayId); + + // Get the display topology + @EnforcePermission("MANAGE_DISPLAYS") + @nullable + DisplayTopology getDisplayTopology(); + + // Set the display topology + @EnforcePermission("MANAGE_DISPLAYS") + void setDisplayTopology(in DisplayTopology topology); } diff --git a/core/java/android/hardware/input/InputSettings.java b/core/java/android/hardware/input/InputSettings.java index 96f6ad117035..71b60cff9367 100644 --- a/core/java/android/hardware/input/InputSettings.java +++ b/core/java/android/hardware/input/InputSettings.java @@ -31,6 +31,7 @@ import static com.android.hardware.input.Flags.touchpadTapDragging; import static com.android.hardware.input.Flags.touchpadThreeFingerTapShortcut; import static com.android.hardware.input.Flags.touchpadVisualizer; import static com.android.hardware.input.Flags.useKeyGestureEventHandler; +import static com.android.hardware.input.Flags.useKeyGestureEventHandlerMultiPressGestures; import static com.android.input.flags.Flags.FLAG_KEYBOARD_REPEAT_KEYS; import static com.android.input.flags.Flags.enableInputFilterRustImpl; import static com.android.input.flags.Flags.keyboardRepeatKeys; @@ -1147,4 +1148,13 @@ public class InputSettings { public static boolean isCustomizableInputGesturesFeatureFlagEnabled() { return enableCustomizableInputGestures() && useKeyGestureEventHandler(); } + + /** + * Whether multi-key gestures are supported using {@code KeyGestureEventHandler} + * + * @hide + */ + public static boolean doesKeyGestureEventHandlerSupportMultiKeyGestures() { + return useKeyGestureEventHandler() && useKeyGestureEventHandlerMultiPressGestures(); + } } diff --git a/core/java/android/hardware/input/KeyGestureEvent.java b/core/java/android/hardware/input/KeyGestureEvent.java index 9d42b67bf5a8..24951c4d516e 100644 --- a/core/java/android/hardware/input/KeyGestureEvent.java +++ b/core/java/android/hardware/input/KeyGestureEvent.java @@ -117,6 +117,10 @@ public final class KeyGestureEvent { public static final int KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW = 69; public static final int KEY_GESTURE_TYPE_MAXIMIZE_FREEFORM_WINDOW = 70; public static final int KEY_GESTURE_TYPE_RESTORE_FREEFORM_WINDOW_SIZE = 71; + public static final int KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_IN = 72; + public static final int KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_OUT = 73; + public static final int KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION = 74; + public static final int KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK = 75; public static final int FLAG_CANCELLED = 1; @@ -203,6 +207,10 @@ public final class KeyGestureEvent { KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW, KEY_GESTURE_TYPE_MAXIMIZE_FREEFORM_WINDOW, KEY_GESTURE_TYPE_RESTORE_FREEFORM_WINDOW_SIZE, + KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_IN, + KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_OUT, + KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION, + KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK, }) @Retention(RetentionPolicy.SOURCE) public @interface KeyGestureType { @@ -773,6 +781,14 @@ public final class KeyGestureEvent { return "KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW"; case KEY_GESTURE_TYPE_RESTORE_FREEFORM_WINDOW_SIZE: return "KEY_GESTURE_TYPE_RESTORE_FREEFORM_WINDOW_SIZE"; + case KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_IN: + return "KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_IN"; + case KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_OUT: + return "KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_OUT"; + case KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION: + return "KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION"; + case KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK: + return "KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK"; default: return Integer.toHexString(value); } diff --git a/core/java/android/hardware/input/input_framework.aconfig b/core/java/android/hardware/input/input_framework.aconfig index 38e32c61c99e..4b2f2c218e5a 100644 --- a/core/java/android/hardware/input/input_framework.aconfig +++ b/core/java/android/hardware/input/input_framework.aconfig @@ -30,14 +30,6 @@ flag { flag { namespace: "input_native" - name: "emoji_and_screenshot_keycodes_available" - is_exported: true - description: "Add new KeyEvent keycodes for opening Emoji Picker and Taking Screenshots" - bug: "315307777" -} - -flag { - namespace: "input_native" name: "keyboard_a11y_slow_keys_flag" description: "Controls if the slow keys accessibility feature for physical keyboard is available to the user" bug: "294546335" @@ -153,9 +145,16 @@ flag { } flag { + name: "enable_new_25q2_keycodes" + namespace: "input" + description: "Enables new 25Q2 keycodes" + bug: "365920375" +} + +flag { name: "override_power_key_behavior_in_focused_window" - namespace: "input_native" - description: "Allows privileged focused windows to capture power key events." + namespace: "wallet_integration" + description: "Allows privileged focused windows to override the power key double tap behavior." bug: "357144512" } diff --git a/core/java/android/hardware/lights/Light.java b/core/java/android/hardware/lights/Light.java index 163f9fa83fe2..38f34e961ec0 100644 --- a/core/java/android/hardware/lights/Light.java +++ b/core/java/android/hardware/lights/Light.java @@ -71,6 +71,12 @@ public final class Light implements Parcelable { public static final int LIGHT_TYPE_KEYBOARD_MIC_MUTE = 10004; /** + * Type for keyboard volume mute light. + * @hide + */ + public static final int LIGHT_TYPE_KEYBOARD_VOLUME_MUTE = 10005; + + /** * Capability for lights that could adjust its LED brightness. If the capability is not present * the LED can only be turned either on or off. */ @@ -99,6 +105,7 @@ public final class Light implements Parcelable { LIGHT_TYPE_PLAYER_ID, LIGHT_TYPE_KEYBOARD_BACKLIGHT, LIGHT_TYPE_KEYBOARD_MIC_MUTE, + LIGHT_TYPE_KEYBOARD_VOLUME_MUTE, }) public @interface LightType {} diff --git a/core/java/android/hardware/location/ContextHubInfo.java b/core/java/android/hardware/location/ContextHubInfo.java index 858ec23ebed8..af715e485b73 100644 --- a/core/java/android/hardware/location/ContextHubInfo.java +++ b/core/java/android/hardware/location/ContextHubInfo.java @@ -15,7 +15,6 @@ */ package android.hardware.location; -import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java index 6284e7061b88..494bfc926384 100644 --- a/core/java/android/hardware/location/ContextHubManager.java +++ b/core/java/android/hardware/location/ContextHubManager.java @@ -18,6 +18,7 @@ package android.hardware.location; import static java.util.Objects.requireNonNull; import android.annotation.CallbackExecutor; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -31,7 +32,6 @@ import android.app.ActivityThread; import android.app.PendingIntent; import android.chre.flags.Flags; import android.content.Context; -import android.content.Intent; import android.content.pm.PackageManager; import android.hardware.contexthub.ErrorCode; import android.os.Handler; @@ -484,15 +484,33 @@ public final class ContextHubManager { } } - /** - * Helper function to generate a stub for a query transaction callback. - * - * @param transaction the transaction to unblock when complete - * - * @return the callback - * - * @hide - */ + /** + * Returns the list of HubInfo objects describing the available hubs (including ContextHub and + * VendorHub). This method is primarily used for debugging purposes as most clients care about + * endpoints and services more than hubs. + * + * @return the list of HubInfo objects + * @see HubInfo + * @hide + */ + @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) + @NonNull + @FlaggedApi(Flags.FLAG_OFFLOAD_API) + public List<HubInfo> getHubs() { + try { + return mService.getHubs(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Helper function to generate a stub for a query transaction callback. + * + * @param transaction the transaction to unblock when complete + * @return the callback + * @hide + */ private IContextHubTransactionCallback createQueryCallback( ContextHubTransaction<List<NanoAppState>> transaction) { return new IContextHubTransactionCallback.Stub() { diff --git a/core/java/android/hardware/location/HubInfo.aidl b/core/java/android/hardware/location/HubInfo.aidl new file mode 100644 index 000000000000..25b5b0aa1222 --- /dev/null +++ b/core/java/android/hardware/location/HubInfo.aidl @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.location; + +/** @hide */ +parcelable HubInfo; diff --git a/core/java/android/hardware/location/HubInfo.java b/core/java/android/hardware/location/HubInfo.java new file mode 100644 index 000000000000..f7de1279672c --- /dev/null +++ b/core/java/android/hardware/location/HubInfo.java @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.hardware.location; + +import android.annotation.FlaggedApi; +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.chre.flags.Flags; +import android.os.BadParcelableException; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Union type for {@link ContextHubInfo} and {@link VendorHubInfo} + * + * @hide + */ +@FlaggedApi(Flags.FLAG_OFFLOAD_API) +public final class HubInfo implements Parcelable { + + @Retention(RetentionPolicy.SOURCE) + @IntDef(value = {TYPE_CONTEXT_HUB, TYPE_VENDOR_HUB}) + private @interface HubType {} + + public static final int TYPE_CONTEXT_HUB = 0; + public static final int TYPE_VENDOR_HUB = 1; + + private final long mId; + @HubType private final int mType; + @Nullable private final ContextHubInfo mContextHubInfo; + @Nullable private final VendorHubInfo mVendorHubInfo; + + /** @hide */ + public HubInfo(long id, @NonNull ContextHubInfo contextHubInfo) { + mId = id; + mType = TYPE_CONTEXT_HUB; + mContextHubInfo = contextHubInfo; + mVendorHubInfo = null; + } + + /** @hide */ + public HubInfo(long id, @NonNull VendorHubInfo vendorHubInfo) { + mId = id; + mType = TYPE_VENDOR_HUB; + mContextHubInfo = null; + mVendorHubInfo = vendorHubInfo; + } + + private HubInfo(Parcel in) { + mId = in.readLong(); + mType = in.readInt(); + + switch (mType) { + case TYPE_CONTEXT_HUB: + mContextHubInfo = ContextHubInfo.CREATOR.createFromParcel(in); + mVendorHubInfo = null; + break; + case TYPE_VENDOR_HUB: + mVendorHubInfo = VendorHubInfo.CREATOR.createFromParcel(in); + mContextHubInfo = null; + break; + default: + throw new BadParcelableException("Parcelable has invalid type"); + } + } + + /** Get the hub unique identifier */ + public long getId() { + return mId; + } + + /** Get the hub type. The type can be {@link TYPE_CONTEXT_HUB} or {@link TYPE_VENDOR_HUB} */ + public int getType() { + return mType; + } + + /** Get the {@link ContextHubInfo} object, null if type is not {@link TYPE_CONTEXT_HUB} */ + @Nullable + public ContextHubInfo getContextHubInfo() { + return mContextHubInfo; + } + + /** Parcel implementation details */ + public int describeContents() { + if (mType == TYPE_CONTEXT_HUB && mContextHubInfo != null) { + return mContextHubInfo.describeContents(); + } + if (mType == TYPE_VENDOR_HUB && mVendorHubInfo != null) { + return mVendorHubInfo.describeContents(); + } + return 0; + } + + /** Parcel implementation details */ + public void writeToParcel(@NonNull Parcel out, int flags) { + out.writeLong(mId); + out.writeInt(mType); + + if (mType == TYPE_CONTEXT_HUB && mContextHubInfo != null) { + mContextHubInfo.writeToParcel(out, flags); + } + + if (mType == TYPE_VENDOR_HUB && mVendorHubInfo != null) { + mVendorHubInfo.writeToParcel(out, flags); + } + } + + @NonNull + @Override + public String toString() { + StringBuilder out = new StringBuilder(); + out.append("HubInfo ID: 0x"); + out.append(Long.toHexString(mId)); + out.append("\n"); + if (mType == TYPE_CONTEXT_HUB && mContextHubInfo != null) { + out.append(" ContextHubDetails: "); + out.append(mContextHubInfo); + } + if (mType == TYPE_VENDOR_HUB && mVendorHubInfo != null) { + out.append(" VendorHubDetails: "); + out.append(mVendorHubInfo); + } + return out.toString(); + } + + public static final @NonNull Creator<HubInfo> CREATOR = + new Creator<>() { + public HubInfo createFromParcel(Parcel in) { + return new HubInfo(in); + } + + public HubInfo[] newArray(int size) { + return new HubInfo[size]; + } + }; +} diff --git a/core/java/android/hardware/location/IContextHubService.aidl b/core/java/android/hardware/location/IContextHubService.aidl index 11f3046150d3..b0cc763dc8fd 100644 --- a/core/java/android/hardware/location/IContextHubService.aidl +++ b/core/java/android/hardware/location/IContextHubService.aidl @@ -18,6 +18,7 @@ package android.hardware.location; // Declare any non-default types here with import statements import android.app.PendingIntent; +import android.hardware.location.HubInfo; import android.hardware.location.ContextHubInfo; import android.hardware.location.ContextHubMessage; import android.hardware.location.NanoApp; @@ -82,6 +83,10 @@ interface IContextHubService { @EnforcePermission("ACCESS_CONTEXT_HUB") List<ContextHubInfo> getContextHubs(); + // Returns a list of HubInfo objects of available hubs (including ContextHub and VendorHub) + @EnforcePermission("ACCESS_CONTEXT_HUB") + List<HubInfo> getHubs(); + // Loads a nanoapp at the specified hub (new API) @EnforcePermission("ACCESS_CONTEXT_HUB") void loadNanoAppOnHub( diff --git a/core/java/android/hardware/location/VendorHubInfo.aidl b/core/java/android/hardware/location/VendorHubInfo.aidl new file mode 100644 index 000000000000..a7936acbb654 --- /dev/null +++ b/core/java/android/hardware/location/VendorHubInfo.aidl @@ -0,0 +1,20 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.location; + +/** @hide */ +parcelable VendorHubInfo;
\ No newline at end of file diff --git a/core/java/android/hardware/location/VendorHubInfo.java b/core/java/android/hardware/location/VendorHubInfo.java new file mode 100644 index 000000000000..26772b18176f --- /dev/null +++ b/core/java/android/hardware/location/VendorHubInfo.java @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.hardware.location; + +import android.annotation.FlaggedApi; +import android.annotation.NonNull; +import android.chre.flags.Flags; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.ParcelableHolder; + +/** + * Information about a VendorHub. VendorHub is similar to ContextHub, but it does not run the + * Context Hub Runtime Environment (or nano apps). It provides a unified endpoint messaging API + * through the ContextHub V4 HAL. + * + * @hide + */ +@FlaggedApi(Flags.FLAG_OFFLOAD_API) +public final class VendorHubInfo implements Parcelable { + private final String mName; + private final int mVersion; + private final ParcelableHolder mExtendedInfo; + + /** @hide */ + public VendorHubInfo(android.hardware.contexthub.VendorHubInfo halHubInfo) { + mName = halHubInfo.name; + mVersion = halHubInfo.version; + mExtendedInfo = halHubInfo.extendedInfo; + } + + private VendorHubInfo(Parcel in) { + mName = in.readString(); + mVersion = in.readInt(); + mExtendedInfo = ParcelableHolder.CREATOR.createFromParcel(in); + } + + /** Get the hub name */ + @NonNull + public String getName() { + return mName; + } + + /** Get the hub version */ + public int getVersion() { + return mVersion; + } + + /** Parcel implementation details */ + public int describeContents() { + return mExtendedInfo.describeContents(); + } + + /** Parcel implementation details */ + public void writeToParcel(@NonNull Parcel out, int flags) { + out.writeString(mName); + out.writeInt(mVersion); + mExtendedInfo.writeToParcel(out, flags); + } + + @NonNull + @Override + public String toString() { + StringBuilder out = new StringBuilder(); + out.append("VendorHub Name : "); + out.append(mName); + out.append(", Version : "); + out.append(mVersion); + return out.toString(); + } + + public static final @NonNull Creator<VendorHubInfo> CREATOR = + new Creator<>() { + public VendorHubInfo createFromParcel(Parcel in) { + return new VendorHubInfo(in); + } + + public VendorHubInfo[] newArray(int size) { + return new VendorHubInfo[size]; + } + }; +} diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java index 92608d048135..d2e232a94622 100644 --- a/core/java/android/hardware/usb/UsbManager.java +++ b/core/java/android/hardware/usb/UsbManager.java @@ -54,6 +54,11 @@ import android.util.Slog; import com.android.internal.annotations.GuardedBy; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; @@ -823,6 +828,216 @@ public class UsbManager { } } + /** + * Opens the handle for accessory, marks it as input or output, and adds it to the map + * if it is the first time the accessory has had an I/O stream associated with it. + */ + private AccessoryHandle openHandleForAccessory(UsbAccessory accessory, + boolean openingInputStream) + throws RemoteException { + synchronized (mAccessoryHandleMapLock) { + if (mAccessoryHandleMap == null) { + mAccessoryHandleMap = new ArrayMap<>(); + } + + // If accessory isn't available in map + if (!mAccessoryHandleMap.containsKey(accessory)) { + // open accessory and store associated AccessoryHandle in map + ParcelFileDescriptor pfd = mService.openAccessory(accessory); + AccessoryHandle newHandle = new AccessoryHandle(pfd, openingInputStream, + !openingInputStream); + mAccessoryHandleMap.put(accessory, newHandle); + + return newHandle; + } + + // if accessory is already in map, get modified handle + AccessoryHandle currentHandle = mAccessoryHandleMap.get(accessory); + if (currentHandle == null) { + throw new IllegalStateException("Accessory doesn't have an associated handle yet!"); + } + + AccessoryHandle modifiedHandle = getModifiedHandleForOpeningStream( + openingInputStream, currentHandle); + + mAccessoryHandleMap.put(accessory, modifiedHandle); + + return modifiedHandle; + } + } + + private AccessoryHandle getModifiedHandleForOpeningStream(boolean openingInputStream, + @NonNull AccessoryHandle currentHandle) { + if (currentHandle.isInputStreamOpened() && openingInputStream) { + throw new IllegalStateException("Input stream already open for this accessory! " + + "Please close the existing input stream before opening a new one."); + } + + if (currentHandle.isOutputStreamOpened() && !openingInputStream) { + throw new IllegalStateException("Output stream already open for this accessory! " + + "Please close the existing output stream before opening a new one."); + } + + boolean isInputStreamOpened = openingInputStream || currentHandle.isInputStreamOpened(); + boolean isOutputStreamOpened = !openingInputStream || currentHandle.isOutputStreamOpened(); + + return new AccessoryHandle( + currentHandle.getPfd(), isInputStreamOpened, isOutputStreamOpened); + } + + /** + * Marks the handle for the given accessory closed for input or output, and closes the handle + * and removes it from the map if there are no more I/O streams associated with the handle. + */ + private void closeHandleForAccessory(UsbAccessory accessory, boolean closingInputStream) + throws IOException { + synchronized (mAccessoryHandleMapLock) { + AccessoryHandle currentHandle = mAccessoryHandleMap.get(accessory); + + if (currentHandle == null) { + throw new IllegalStateException( + "No handle has been initialised for this accessory!"); + } + + AccessoryHandle modifiedHandle = getModifiedHandleForClosingStream( + closingInputStream, currentHandle); + if (!modifiedHandle.isOpen()) { + //close handle and remove accessory handle pair from map + modifiedHandle.getPfd().close(); + mAccessoryHandleMap.remove(accessory); + } else { + mAccessoryHandleMap.put(accessory, modifiedHandle); + } + } + } + + private AccessoryHandle getModifiedHandleForClosingStream(boolean closingInputStream, + @NonNull AccessoryHandle currentHandle) { + if (!currentHandle.isInputStreamOpened() && closingInputStream) { + throw new IllegalStateException( + "Attempting to close an input stream that has not been opened " + + "for this accessory!"); + } + + if (!currentHandle.isOutputStreamOpened() && !closingInputStream) { + throw new IllegalStateException( + "Attempting to close an output stream that has not been opened " + + "for this accessory!"); + } + + boolean isInputStreamOpened = !closingInputStream && currentHandle.isInputStreamOpened(); + boolean isOutputStreamOpened = closingInputStream && currentHandle.isOutputStreamOpened(); + + return new AccessoryHandle( + currentHandle.getPfd(), isInputStreamOpened, isOutputStreamOpened); + } + + /** + * An InputStream you can create on a UsbAccessory, which will + * take care of calling {@link ParcelFileDescriptor#close + * ParcelFileDescriptor.close()} for you when the stream is closed. + */ + private class AccessoryAutoCloseInputStream extends FileInputStream { + + private final ParcelFileDescriptor mPfd; + private final UsbAccessory mAccessory; + + AccessoryAutoCloseInputStream(UsbAccessory accessory, ParcelFileDescriptor pfd) { + super(pfd.getFileDescriptor()); + this.mAccessory = accessory; + this.mPfd = pfd; + } + + @Override + public void close() throws IOException { + /* TODO(b/377850642) : Ensure the stream is closed even if client does not + explicitly close the stream to avoid corrupt FDs*/ + super.close(); + closeHandleForAccessory(mAccessory, true); + } + + + @Override + public int read() throws IOException { + final int result = super.read(); + checkError(result); + return result; + } + + @Override + public int read(byte[] b) throws IOException { + final int result = super.read(b); + checkError(result); + return result; + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + final int result = super.read(b, off, len); + checkError(result); + return result; + } + + private void checkError(int result) throws IOException { + if (result == -1 && mPfd.canDetectErrors()) { + mPfd.checkError(); + } + } + } + + /** + * An OutputStream you can create on a UsbAccessory, which will + * take care of calling {@link ParcelFileDescriptor#close + * ParcelFileDescriptor.close()} for you when the stream is closed. + */ + private class AccessoryAutoCloseOutputStream extends FileOutputStream { + private final UsbAccessory mAccessory; + + AccessoryAutoCloseOutputStream(UsbAccessory accessory, ParcelFileDescriptor pfd) { + super(pfd.getFileDescriptor()); + mAccessory = accessory; + } + + @Override + public void close() throws IOException { + /* TODO(b/377850642) : Ensure the stream is closed even if client does not + explicitly close the stream to avoid corrupt FDs*/ + super.close(); + closeHandleForAccessory(mAccessory, false); + } + } + + /** + * Holds file descriptor and marks whether input and output streams have been opened for it. + */ + private static class AccessoryHandle { + private final ParcelFileDescriptor mPfd; + private final boolean mInputStreamOpened; + private final boolean mOutputStreamOpened; + AccessoryHandle(ParcelFileDescriptor parcelFileDescriptor, + boolean inputStreamOpened, boolean outputStreamOpened) { + mPfd = parcelFileDescriptor; + mInputStreamOpened = inputStreamOpened; + mOutputStreamOpened = outputStreamOpened; + } + + public ParcelFileDescriptor getPfd() { + return mPfd; + } + + public boolean isInputStreamOpened() { + return mInputStreamOpened; + } + + public boolean isOutputStreamOpened() { + return mOutputStreamOpened; + } + + public boolean isOpen() { + return (mInputStreamOpened || mOutputStreamOpened); + } + } + private final Context mContext; private final IUsbManager mService; private final Object mDisplayPortListenersLock = new Object(); @@ -831,6 +1046,11 @@ public class UsbManager { @GuardedBy("mDisplayPortListenersLock") private DisplayPortAltModeInfoDispatchingListener mDisplayPortServiceListener; + private final Object mAccessoryHandleMapLock = new Object(); + @GuardedBy("mAccessoryHandleMapLock") + private ArrayMap<UsbAccessory, AccessoryHandle> mAccessoryHandleMap; + + /** * @hide */ @@ -922,6 +1142,10 @@ public class UsbManager { * data of a USB transfer should be read at once. If only a partial request is read the rest of * the transfer is dropped. * + * <p>It is strongly recommended to use newer methods instead of this method, + * since this method may provide sub-optimal performance on some devices. + * This method could potentially face interim performance degradation as well. + * * @param accessory the USB accessory to open * @return file descriptor, or null if the accessory could not be opened. */ @@ -935,6 +1159,49 @@ public class UsbManager { } /** + * Opens an input stream for reading from the USB accessory. + * If accessory is not open at this point, accessory will first be opened. + * <p>If data is read from the created {@link java.io.InputStream} all + * data of a USB transfer should be read at once. If only a partial request is read, the rest of + * the transfer is dropped. + * <p>The caller is responsible for ensuring that the returned stream is closed. + * + * @param accessory the USB accessory to open an input stream for + * @return input stream to read from given USB accessory + */ + @FlaggedApi(Flags.FLAG_ENABLE_ACCESSORY_STREAM_API) + @RequiresFeature(PackageManager.FEATURE_USB_ACCESSORY) + public @NonNull InputStream openAccessoryInputStream(@NonNull UsbAccessory accessory) { + try { + return new AccessoryAutoCloseInputStream(accessory, + openHandleForAccessory(accessory, true).getPfd()); + + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Opens an output stream for writing to the USB accessory. + * If accessory is not open at this point, accessory will first be opened. + * <p>The caller is responsible for ensuring that the returned stream is closed. + * + * @param accessory the USB accessory to open an output stream for + * @return output stream to write to given USB accessory + */ + @FlaggedApi(Flags.FLAG_ENABLE_ACCESSORY_STREAM_API) + @RequiresFeature(PackageManager.FEATURE_USB_ACCESSORY) + public @NonNull OutputStream openAccessoryOutputStream(@NonNull UsbAccessory accessory) { + try { + return new AccessoryAutoCloseOutputStream(accessory, + openHandleForAccessory(accessory, false).getPfd()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + + } + + /** * Gets the functionfs control file descriptor for the given function, with * the usb descriptors and strings already written. The file descriptor is used * by the function implementation to handle events and control requests. @@ -1293,7 +1560,7 @@ public class UsbManager { * <p> * This function returns the current USB bandwidth through USB Gadget HAL. * It should be used when Android device is in USB peripheral mode and - * connects to a USB host. If USB state is not configued, API will return + * connects to a USB host. If USB state is not configured, API will return * {@value #USB_DATA_TRANSFER_RATE_UNKNOWN}. In addition, the unit of the * return value is Mbps. * </p> diff --git a/core/java/android/hardware/usb/flags/usb_framework_flags.aconfig b/core/java/android/hardware/usb/flags/usb_framework_flags.aconfig index 3b7a9e95c521..b719a7c6daac 100644 --- a/core/java/android/hardware/usb/flags/usb_framework_flags.aconfig +++ b/core/java/android/hardware/usb/flags/usb_framework_flags.aconfig @@ -31,3 +31,11 @@ flag { description: "Feature flag to enable exposing usb speed system api" bug: "373653182" } + +flag { + name: "enable_accessory_stream_api" + is_exported: true + namespace: "usb" + description: "Feature flag to enable stream APIs for Accessory mode" + bug: "369356693" +} diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java index c18fb0c38b82..99e7d166446e 100644 --- a/core/java/android/os/Bundle.java +++ b/core/java/android/os/Bundle.java @@ -281,7 +281,7 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable { } /** {@hide} */ - public void setIsIntentExtra() { + public void enableTokenVerification() { mFlags |= FLAG_VERIFY_TOKENS_PRESENT; } diff --git a/core/java/android/os/CombinedMessageQueue/MessageQueue.java b/core/java/android/os/CombinedMessageQueue/MessageQueue.java index acda0c5664fe..7529ab9ab894 100644 --- a/core/java/android/os/CombinedMessageQueue/MessageQueue.java +++ b/core/java/android/os/CombinedMessageQueue/MessageQueue.java @@ -31,6 +31,7 @@ import android.util.SparseArray; import android.util.proto.ProtoOutputStream; import dalvik.annotation.optimization.NeverCompile; +import dalvik.system.VMDebug; import java.io.FileDescriptor; import java.lang.annotation.Retention; @@ -55,7 +56,7 @@ import java.util.concurrent.locks.ReentrantLock; * {@link Looper#myQueue() Looper.myQueue()}. */ @RavenwoodKeepWholeClass -@RavenwoodRedirectionClass("MessageQueue_host") +@RavenwoodRedirectionClass("MessageQueue_ravenwood") public final class MessageQueue { private static final String TAG_L = "LegacyMessageQueue"; private static final String TAG_C = "ConcurrentMessageQueue"; @@ -110,7 +111,7 @@ public final class MessageQueue { private native static void nativeSetFileDescriptorEvents(long ptr, int fd, int events); MessageQueue(boolean quitAllowed) { - mUseConcurrent = UserHandle.isCore(Process.myUid()); + mUseConcurrent = UserHandle.isCore(Process.myUid()) && !VMDebug.isDebuggingEnabled(); mQuitAllowed = quitAllowed; mPtr = nativeInit(); } diff --git a/core/java/android/os/ConcurrentMessageQueue/MessageQueue.java b/core/java/android/os/ConcurrentMessageQueue/MessageQueue.java index 9db88d17614b..c2a47d767801 100644 --- a/core/java/android/os/ConcurrentMessageQueue/MessageQueue.java +++ b/core/java/android/os/ConcurrentMessageQueue/MessageQueue.java @@ -54,7 +54,7 @@ import java.util.concurrent.locks.ReentrantLock; * {@link Looper#myQueue() Looper.myQueue()}. */ @RavenwoodKeepWholeClass -@RavenwoodRedirectionClass("MessageQueue_host") +@RavenwoodRedirectionClass("MessageQueue_ravenwood") public final class MessageQueue { private static final String TAG = "ConcurrentMessageQueue"; private static final boolean DEBUG = false; diff --git a/core/java/android/os/LegacyMessageQueue/MessageQueue.java b/core/java/android/os/LegacyMessageQueue/MessageQueue.java index 9f7b0b71ae95..cae82d010132 100644 --- a/core/java/android/os/LegacyMessageQueue/MessageQueue.java +++ b/core/java/android/os/LegacyMessageQueue/MessageQueue.java @@ -45,7 +45,7 @@ import java.util.concurrent.atomic.AtomicLong; * {@link Looper#myQueue() Looper.myQueue()}. */ @RavenwoodKeepWholeClass -@RavenwoodRedirectionClass("MessageQueue_host") +@RavenwoodRedirectionClass("MessageQueue_ravenwood") public final class MessageQueue { private static final String TAG = "MessageQueue"; private static final boolean DEBUG = false; diff --git a/core/java/android/os/LockedMessageQueue/MessageQueue.java b/core/java/android/os/LockedMessageQueue/MessageQueue.java index f3eec13cb20f..2401f3d11bcf 100644 --- a/core/java/android/os/LockedMessageQueue/MessageQueue.java +++ b/core/java/android/os/LockedMessageQueue/MessageQueue.java @@ -48,7 +48,7 @@ import java.util.concurrent.atomic.AtomicLong; * {@link Looper#myQueue() Looper.myQueue()}. */ @RavenwoodKeepWholeClass -@RavenwoodRedirectionClass("MessageQueue_host") +@RavenwoodRedirectionClass("MessageQueue_ravenwood") public final class MessageQueue { private static final String TAG = "LockedMessageQueue"; private static final boolean DEBUG = false; diff --git a/ravenwood/runtime-helper-src/framework/android/os/MessageQueue_host.java b/core/java/android/os/MessageQueue_ravenwood.java index 1b63adc4319f..4033707c3253 100644 --- a/ravenwood/runtime-helper-src/framework/android/os/MessageQueue_host.java +++ b/core/java/android/os/MessageQueue_ravenwood.java @@ -16,13 +16,16 @@ package android.os; +import android.ravenwood.annotation.RavenwoodKeepWholeClass; + import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicLong; -public class MessageQueue_host { +@RavenwoodKeepWholeClass +class MessageQueue_ravenwood { private static final AtomicLong sNextId = new AtomicLong(1); - private static final Map<Long, MessageQueue_host> sInstances = new ConcurrentHashMap<>(); + private static final Map<Long, MessageQueue_ravenwood> sInstances = new ConcurrentHashMap<>(); private boolean mDeleted = false; @@ -37,8 +40,8 @@ public class MessageQueue_host { } } - private static MessageQueue_host getInstance(long id) { - MessageQueue_host q = sInstances.get(id); + private static MessageQueue_ravenwood getInstance(long id) { + MessageQueue_ravenwood q = sInstances.get(id); if (q == null) { throw new RuntimeException("MessageQueue doesn't exist with id=" + id); } @@ -48,7 +51,7 @@ public class MessageQueue_host { public static long nativeInit() { final long id = sNextId.getAndIncrement(); - final MessageQueue_host q = new MessageQueue_host(); + final MessageQueue_ravenwood q = new MessageQueue_ravenwood(); sInstances.put(id, q); return id; } diff --git a/core/java/android/os/RemoteCallbackList.java b/core/java/android/os/RemoteCallbackList.java index 01b1e5e11332..91c482faf7d7 100644 --- a/core/java/android/os/RemoteCallbackList.java +++ b/core/java/android/os/RemoteCallbackList.java @@ -194,15 +194,27 @@ public class RemoteCallbackList<E extends IInterface> { } } - public void maybeSubscribeToFrozenCallback() throws RemoteException { + void maybeSubscribeToFrozenCallback() throws RemoteException { if (mFrozenCalleePolicy != FROZEN_CALLEE_POLICY_UNSET) { - mBinder.addFrozenStateChangeCallback(this); + try { + mBinder.addFrozenStateChangeCallback(this); + } catch (UnsupportedOperationException e) { + // The kernel does not support frozen notifications. In this case we want to + // silently fall back to FROZEN_CALLEE_POLICY_UNSET. This is done by simply + // ignoring the error and moving on. mCurrentState would always be + // STATE_UNFROZEN and all callbacks are invoked immediately. + } } } - public void maybeUnsubscribeFromFrozenCallback() { + void maybeUnsubscribeFromFrozenCallback() { if (mFrozenCalleePolicy != FROZEN_CALLEE_POLICY_UNSET) { - mBinder.removeFrozenStateChangeCallback(this); + try { + mBinder.removeFrozenStateChangeCallback(this); + } catch (UnsupportedOperationException e) { + // The kernel does not support frozen notifications. Ignore the error and move + // on. + } } } diff --git a/core/java/android/os/SemiConcurrentMessageQueue/MessageQueue.java b/core/java/android/os/SemiConcurrentMessageQueue/MessageQueue.java index db323dcf9009..435c34f832c6 100644 --- a/core/java/android/os/SemiConcurrentMessageQueue/MessageQueue.java +++ b/core/java/android/os/SemiConcurrentMessageQueue/MessageQueue.java @@ -52,7 +52,7 @@ import java.util.concurrent.atomic.AtomicLong; * {@link Looper#myQueue() Looper.myQueue()}. */ @RavenwoodKeepWholeClass -@RavenwoodRedirectionClass("MessageQueue_host") +@RavenwoodRedirectionClass("MessageQueue_ravenwood") public final class MessageQueue { private static final String TAG = "SemiConcurrentMessageQueue"; private static final boolean DEBUG = false; diff --git a/core/java/android/os/WorkSource.java b/core/java/android/os/WorkSource.java index 6d4e28403908..517418a717fb 100644 --- a/core/java/android/os/WorkSource.java +++ b/core/java/android/os/WorkSource.java @@ -1011,13 +1011,7 @@ public class WorkSource implements Parcelable { return mTags.length > 0 ? mTags[0] : null; } - // TODO: The following three trivial getters are purely for testing and will be removed - // once we have higher level logic in place, e.g for serializing this WorkChain to a proto, - // diffing it etc. - - /** @hide */ - @VisibleForTesting public int[] getUids() { int[] uids = new int[mSize]; System.arraycopy(mUids, 0, uids, 0, mSize); @@ -1025,7 +1019,6 @@ public class WorkSource implements Parcelable { } /** @hide */ - @VisibleForTesting public String[] getTags() { String[] tags = new String[mSize]; System.arraycopy(mTags, 0, tags, 0, mSize); @@ -1033,7 +1026,6 @@ public class WorkSource implements Parcelable { } /** @hide */ - @VisibleForTesting public int getSize() { return mSize; } diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig index 9c83bc2c88ec..d9db28e0b3c3 100644 --- a/core/java/android/os/flags.aconfig +++ b/core/java/android/os/flags.aconfig @@ -243,6 +243,15 @@ flag { } flag { + name: "update_engine_api" + namespace: "art_mainline" + description: "Update Engine APIs for ART" + is_exported: true + is_fixed_read_only: true + bug: "377557749" +} + +flag { namespace: "system_performance" name: "perfetto_sdk_tracing" description: "Tracing using Perfetto SDK." diff --git a/core/java/android/os/instrumentation/ExecutableMethodFileOffsets.aidl b/core/java/android/os/instrumentation/ExecutableMethodFileOffsets.aidl new file mode 100644 index 000000000000..dbe54891b0f2 --- /dev/null +++ b/core/java/android/os/instrumentation/ExecutableMethodFileOffsets.aidl @@ -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 android.os.instrumentation; + +/** + * Represents the location of the code for a compiled method within a process' + * memory. + * {@hide} + */ +@JavaDerive(toString=true) +parcelable ExecutableMethodFileOffsets { + /** + * The OS path of the containing file (could be virtual). + */ + @utf8InCpp String containerPath; + /** + * The offset of the containing file within the process' memory. + */ + long containerOffset; + /** + * The offset of the method within the containing file. + */ + long methodOffset; +} diff --git a/core/java/android/os/instrumentation/IDynamicInstrumentationManager.aidl b/core/java/android/os/instrumentation/IDynamicInstrumentationManager.aidl new file mode 100644 index 000000000000..c45c51d15cc9 --- /dev/null +++ b/core/java/android/os/instrumentation/IDynamicInstrumentationManager.aidl @@ -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 android.os.instrumentation; + +import android.os.instrumentation.ExecutableMethodFileOffsets; +import android.os.instrumentation.MethodDescriptor; +import android.os.instrumentation.TargetProcess; + +/** + * System private API for managing the dynamic attachment of instrumentation. + * + * {@hide} + */ +interface IDynamicInstrumentationManager { + /** Provides ART metadata about the described compiled method within the target process */ + @PermissionManuallyEnforced + @nullable ExecutableMethodFileOffsets getExecutableMethodFileOffsets( + in TargetProcess targetProcess, in MethodDescriptor methodDescriptor); +} diff --git a/core/java/android/os/instrumentation/MethodDescriptor.aidl b/core/java/android/os/instrumentation/MethodDescriptor.aidl new file mode 100644 index 000000000000..055d0ecb66e4 --- /dev/null +++ b/core/java/android/os/instrumentation/MethodDescriptor.aidl @@ -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 android.os.instrumentation; + +/** + * Represents a JVM method, where class fields that make up its signature. + * {@hide} + */ +@JavaDerive(toString=true) +parcelable MethodDescriptor { + /** + * Fully qualified class in reverse.domain.Naming + */ + @utf8InCpp String fullyQualifiedClassName; + /** + * Name of the method. + */ + @utf8InCpp String methodName; + /** + * Fully qualified types of method parameters, or string representations if primitive e.g. "int". + */ + @utf8InCpp String[] fullyQualifiedParameters; +} diff --git a/core/java/android/os/instrumentation/TargetProcess.aidl b/core/java/android/os/instrumentation/TargetProcess.aidl new file mode 100644 index 000000000000..e90780d07ef2 --- /dev/null +++ b/core/java/android/os/instrumentation/TargetProcess.aidl @@ -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 android.os.instrumentation; + +/** + * Addresses a process that would run on the device. + * Helps disambiguate targeted processes in cases of pid re-use. + * {@hide} + */ +@JavaDerive(toString=true) +parcelable TargetProcess { + int uid; + int pid; + @utf8InCpp String processName; +} diff --git a/core/java/android/permission/flags.aconfig b/core/java/android/permission/flags.aconfig index 9e0d0e195a96..33040be7c0fb 100644 --- a/core/java/android/permission/flags.aconfig +++ b/core/java/android/permission/flags.aconfig @@ -284,7 +284,7 @@ flag { is_fixed_read_only: true is_exported: true namespace: "android_health_services" - description: "This fixed read-only flag is used to enable replacing permission BODY_SENSORS (and BODY_SENSORS_BACKGROUND) with granular health permission READ_HEART_RATE (and READ_HEALTH_DATA_IN_BACKGROUND)" + description: "Enables replacement of BODY_SENSORS/BODY_SENSORS_BACKGROUND permissions with granular health permissions READ_HEART_RATE, READ_SKIN_TEMPERATURE, READ_OXYGEN_SATURATION, and READ_HEALTH_DATA_IN_BACKGROUND" bug: "364638912" } @@ -300,24 +300,6 @@ flag { } flag { - name: "platform_skin_temperature_enabled" - is_fixed_read_only: true - is_exported: true - namespace: "android_health_services" - description: "This fixed read-only flag is used to enable platform support for Skin Temperature." - bug: "369872443" -} - -flag { - name: "platform_oxygen_saturation_enabled" - is_fixed_read_only: true - is_exported: true - namespace: "android_health_services" - description: "This fixed read-only flag is used to enable platform support for Oxygen Saturation (SpO2)." - bug: "369873227" -} - -flag { name: "allow_host_permission_dialogs_on_virtual_devices" is_exported: true namespace: "permissions" @@ -354,9 +336,10 @@ flag { flag { name: "health_connect_backup_restore_permission_enabled" is_fixed_read_only: true - namespace: "health_connect" + namespace: "health_fitness_aconfig" description: "This flag protects the permission that is required to call Health Connect backup and restore apis" bug: "376014879" # android_fr bug + is_exported: true } flag { @@ -385,3 +368,26 @@ flag { description: "This fixed read-only flag is used to enable new ranging permission for all ranging use cases." bug: "370977414" } + +flag { + name: "system_selection_toolbar_enabled" + namespace: "permissions" + description: "Enables the system selection toolbar feature." + bug: "363318732" +} + +flag { + name: "use_system_selection_toolbar_in_sysui" + namespace: "permissions" + description: "Uses the SysUi process to host the SelectionToolbarRenderService." + bug: "363318732" +} + +flag{ + name: "note_op_batching_enabled" + is_fixed_read_only: true + is_exported: true + namespace: "permissions" + description: "Batch noteOperations on the client to reduce binder call volume" + bug: "366013082" +} diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index d19681c86320..d2a20b65c1ee 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -1217,6 +1217,69 @@ public final class Settings { "android.settings.REGIONAL_PREFERENCES_SETTINGS"; /** + * Activity Action: Show screen for allowing the region configuration. + * <p> + * Input: Nothing. + * <p> + * Output: Nothing. + */ + @FlaggedApi(Flags.FLAG_SYSTEM_REGIONAL_PREFERENCES_API_ENABLED) + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_REGION_SETTINGS = + "android.settings.REGION_SETTINGS"; + + /** + * Activity Action: Show first day of week configuration settings. + * <p> + * Input: Nothing. + * <p> + * Output: Nothing. + */ + @FlaggedApi(Flags.FLAG_SYSTEM_REGIONAL_PREFERENCES_API_ENABLED) + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_FIRST_DAY_OF_WEEK_SETTINGS = + "android.settings.FIRST_DAY_OF_WEEK_SETTINGS"; + + /** + * Activity Action: Show temperature unit configuration settings. + * <p> + * Input: Nothing. + * <p> + * Output: Nothing. + */ + @FlaggedApi(Flags.FLAG_SYSTEM_REGIONAL_PREFERENCES_API_ENABLED) + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_TEMPERATURE_UNIT_SETTINGS = + "android.settings.TEMPERATURE_UNIT_SETTINGS"; + + /** + * Activity Action: Show numbering system configuration settings. + * <p> + * In some cases, a matching Activity may not exist, so ensure you + * safeguard against this. + * <p> + * Input: Nothing. + * <p> + * Output: Nothing. + */ + @FlaggedApi(Flags.FLAG_SYSTEM_REGIONAL_PREFERENCES_API_ENABLED) + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_NUMBERING_SYSTEM_SETTINGS = + "android.settings.NUMBERING_SYSTEM_SETTINGS"; + + /** + * Activity Action: Show measurement system configuration settings. + * <p> + * Input: Nothing. + * <p> + * Output: Nothing. + */ + @FlaggedApi(Flags.FLAG_SYSTEM_REGIONAL_PREFERENCES_API_ENABLED) + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_MEASUREMENT_SYSTEM_SETTINGS = + "android.settings.MEASUREMENT_SYSTEM_SETTINGS"; + + /** * Activity Action: Show settings to allow configuration of lockscreen. * <p> * In some cases, a matching Activity may not exist, so ensure you @@ -8687,6 +8750,19 @@ public final class Settings { public static final String ACCESSIBILITY_QS_TARGETS = "accessibility_qs_targets"; /** + * Setting specifying the accessibility services, accessibility shortcut targets, + * or features to be toggled via a keyboard shortcut gesture. + * + * <p> This is a colon-separated string list which contains the flattened + * {@link ComponentName} and the class name of a system class implementing a supported + * accessibility feature. + * + * @hide + */ + public static final String ACCESSIBILITY_KEY_GESTURE_TARGETS = + "accessibility_key_gesture_targets"; + + /** * The system class name of magnification controller which is a target to be toggled via * accessibility shortcut or accessibility button. * diff --git a/core/java/android/provider/flags.aconfig b/core/java/android/provider/flags.aconfig index 4c636735b5ce..fff53637485b 100644 --- a/core/java/android/provider/flags.aconfig +++ b/core/java/android/provider/flags.aconfig @@ -63,3 +63,11 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "system_regional_preferences_api_enabled" + is_exported: true + namespace: "globalintl" + description: "Feature flag for regional preferences APIs" + bug: "370379000" +} diff --git a/core/java/android/security/advancedprotection/AdvancedProtectionManager.java b/core/java/android/security/advancedprotection/AdvancedProtectionManager.java index 6f3e3d8f0d3b..9fe0dda136d1 100644 --- a/core/java/android/security/advancedprotection/AdvancedProtectionManager.java +++ b/core/java/android/security/advancedprotection/AdvancedProtectionManager.java @@ -16,20 +16,30 @@ package android.security.advancedprotection; +import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; + import android.Manifest; import android.annotation.CallbackExecutor; import android.annotation.FlaggedApi; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.RequiresPermission; +import android.annotation.SdkConstant; +import android.annotation.StringDef; import android.annotation.SystemApi; import android.annotation.SystemService; import android.content.Context; +import android.content.Intent; import android.os.Binder; import android.os.RemoteException; import android.security.Flags; import android.util.Log; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.List; +import java.util.Objects; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; @@ -45,6 +55,139 @@ import java.util.concurrent.Executor; public final class AdvancedProtectionManager { private static final String TAG = "AdvancedProtectionMgr"; + /** + * Advanced Protection's identifier for setting policies or restrictions in DevicePolicyManager. + * + * @hide */ + public static final String ADVANCED_PROTECTION_SYSTEM_ENTITY = + "android.security.advancedprotection"; + + /** + * Feature identifier for disallowing 2G. + * + * @hide */ + @SystemApi + public static final String FEATURE_ID_DISALLOW_CELLULAR_2G = + "android.security.advancedprotection.feature_disallow_2g"; + + /** + * Feature identifier for disallowing install of unknown sources. + * + * @hide */ + @SystemApi + public static final String FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES = + "android.security.advancedprotection.feature_disallow_install_unknown_sources"; + + /** + * Feature identifier for disallowing USB. + * + * @hide */ + @SystemApi + public static final String FEATURE_ID_DISALLOW_USB = + "android.security.advancedprotection.feature_disallow_usb"; + + /** + * Feature identifier for disallowing WEP. + * + * @hide */ + @SystemApi + public static final String FEATURE_ID_DISALLOW_WEP = + "android.security.advancedprotection.feature_disallow_wep"; + + /** + * Feature identifier for enabling MTE. + * + * @hide */ + @SystemApi + public static final String FEATURE_ID_ENABLE_MTE = + "android.security.advancedprotection.feature_enable_mte"; + + /** @hide */ + @StringDef(prefix = { "FEATURE_ID_" }, value = { + FEATURE_ID_DISALLOW_CELLULAR_2G, + FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES, + FEATURE_ID_DISALLOW_USB, + FEATURE_ID_DISALLOW_WEP, + FEATURE_ID_ENABLE_MTE, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface FeatureId {} + + private static final Set<String> ALL_FEATURE_IDS = Set.of( + FEATURE_ID_DISALLOW_CELLULAR_2G, + FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES, + FEATURE_ID_DISALLOW_USB, + FEATURE_ID_DISALLOW_WEP, + FEATURE_ID_ENABLE_MTE); + + /** + * Activity Action: Show a dialog with disabled by advanced protection message. + * <p> If a user action or a setting toggle is disabled by advanced protection, this dialog can + * be triggered to let the user know about this. + * <p> + * Input: + * <p>{@link #EXTRA_SUPPORT_DIALOG_FEATURE}: The feature identifier. + * <p>{@link #EXTRA_SUPPORT_DIALOG_TYPE}: The type of the action. + * <p> + * Output: Nothing. + * + * @hide */ + @SystemApi + @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION) + @FlaggedApi(android.security.Flags.FLAG_AAPM_API) + public static final String ACTION_SHOW_ADVANCED_PROTECTION_SUPPORT_DIALOG = + "android.security.advancedprotection.action.SHOW_ADVANCED_PROTECTION_SUPPORT_DIALOG"; + + /** + * A string extra used with {@link #createSupportIntent} to identify the feature that needs to + * show a support dialog explaining it was disabled by advanced protection. + * + * @hide */ + @FeatureId + @SystemApi + public static final String EXTRA_SUPPORT_DIALOG_FEATURE = + "android.security.advancedprotection.extra.SUPPORT_DIALOG_FEATURE"; + + /** + * A string extra used with {@link #createSupportIntent} to identify the type of the action that + * needs to be explained in the support dialog. + * + * @hide */ + @SupportDialogType + @SystemApi + public static final String EXTRA_SUPPORT_DIALOG_TYPE = + "android.security.advancedprotection.extra.SUPPORT_DIALOG_TYPE"; + + /** + * Type for {@link #EXTRA_SUPPORT_DIALOG_TYPE} indicating a user performed an action that was + * blocked by advanced protection. + * + * @hide */ + @SystemApi + public static final String SUPPORT_DIALOG_TYPE_BLOCKED_INTERACTION = + "android.security.advancedprotection.type_blocked_interaction"; + + /** + * Type for {@link #EXTRA_SUPPORT_DIALOG_TYPE} indicating a user pressed on a setting toggle + * that was disabled by advanced protection. + * + * @hide */ + @SystemApi + public static final String SUPPORT_DIALOG_TYPE_DISABLED_SETTING = + "android.security.advancedprotection.type_disabled_setting"; + + /** @hide */ + @StringDef(prefix = { "SUPPORT_DIALOG_TYPE_" }, value = { + SUPPORT_DIALOG_TYPE_BLOCKED_INTERACTION, + SUPPORT_DIALOG_TYPE_DISABLED_SETTING, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface SupportDialogType {} + + private static final Set<String> ALL_SUPPORT_DIALOG_TYPES = Set.of( + SUPPORT_DIALOG_TYPE_BLOCKED_INTERACTION, + SUPPORT_DIALOG_TYPE_DISABLED_SETTING); + private final ConcurrentHashMap<Callback, IAdvancedProtectionCallback> mCallbackMap = new ConcurrentHashMap<>(); @@ -164,6 +307,43 @@ public final class AdvancedProtectionManager { } /** + * Called by a feature to display a support dialog when a feature was disabled by advanced + * protection. This returns an intent that can be used with + * {@link Context#startActivity(Intent)} to display the dialog. + * + * <p>Note that this method doesn't check if the feature is actually disabled, i.e. this method + * will always return an intent. + * + * @param featureId The feature identifier. + * @param type The type of the feature describing the action that needs to be explained + * in the dialog or null for default explanation. + * @return Intent An intent to be used to start the dialog-activity that explains a feature was + * disabled by advanced protection. + * @hide + */ + @SystemApi + public @NonNull Intent createSupportIntent(@NonNull @FeatureId String featureId, + @Nullable @SupportDialogType String type) { + Objects.requireNonNull(featureId); + if (!ALL_FEATURE_IDS.contains(featureId)) { + throw new IllegalArgumentException(featureId + " is not a valid feature ID. See" + + " FEATURE_ID_* APIs."); + } + if (type != null && !ALL_SUPPORT_DIALOG_TYPES.contains(type)) { + throw new IllegalArgumentException(type + " is not a valid type. See" + + " SUPPORT_DIALOG_TYPE_* APIs."); + } + + Intent intent = new Intent(ACTION_SHOW_ADVANCED_PROTECTION_SUPPORT_DIALOG); + intent.setFlags(FLAG_ACTIVITY_NEW_TASK); + intent.putExtra(EXTRA_SUPPORT_DIALOG_FEATURE, featureId); + if (type != null) { + intent.putExtra(EXTRA_SUPPORT_DIALOG_TYPE, type); + } + return intent; + } + + /** * A callback class for monitoring changes to Advanced Protection state * * <p>To register a callback, implement this interface, and register it with diff --git a/core/java/android/security/flags.aconfig b/core/java/android/security/flags.aconfig index 1d35344e5218..ce901217d700 100644 --- a/core/java/android/security/flags.aconfig +++ b/core/java/android/security/flags.aconfig @@ -109,7 +109,7 @@ flag { flag { name: "afl_api" - namespace: "platform_security" + namespace: "hardware_backed_security" description: "AFL feature" bug: "365994454" } @@ -120,3 +120,10 @@ flag { description: "Feature flag for exposing KeyStore grant APIs" bug: "351158708" } + +flag { + name: "secure_lockdown" + namespace: "biometrics" + description: "Feature flag for Secure Lockdown feature" + bug: "373422357" +}
\ No newline at end of file diff --git a/core/java/android/service/autofill/FillRequest.java b/core/java/android/service/autofill/FillRequest.java index ca20801852f1..be4629ab8178 100644 --- a/core/java/android/service/autofill/FillRequest.java +++ b/core/java/android/service/autofill/FillRequest.java @@ -16,6 +16,9 @@ package android.service.autofill; +import static android.service.autofill.Flags.FLAG_FILL_DIALOG_IMPROVEMENTS; + +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -100,7 +103,12 @@ public final class FillRequest implements Parcelable { /** * Indicates the request supports fill dialog presentation for the fields, the * system will send the request when the activity just started. + * + * @deprecated All requests would support fill dialog by default. + * Presence of this flag isn't needed. */ + @FlaggedApi(FLAG_FILL_DIALOG_IMPROVEMENTS) + @Deprecated public static final @RequestFlags int FLAG_SUPPORTS_FILL_DIALOG = 0x40; /** @@ -588,10 +596,10 @@ public final class FillRequest implements Parcelable { }; @DataClass.Generated( - time = 1701010178309L, + time = 1730991738865L, codegenVersion = "1.0.23", sourceFile = "frameworks/base/core/java/android/service/autofill/FillRequest.java", - inputSignatures = "public static final @android.service.autofill.FillRequest.RequestFlags int FLAG_MANUAL_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_PASSWORD_INPUT_TYPE\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_VIEW_NOT_FOCUSED\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_SUPPORTS_FILL_DIALOG\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_IME_SHOWING\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_RESET_FILL_DIALOG_STATE\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_PCC_DETECTION\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_SCREEN_HAS_CREDMAN_FIELD\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_VIEW_REQUESTS_CREDMAN_SERVICE\npublic static final int INVALID_REQUEST_ID\nprivate final int mId\nprivate final @android.annotation.NonNull java.util.List<android.service.autofill.FillContext> mFillContexts\nprivate final @android.annotation.NonNull java.util.List<java.lang.String> mHints\nprivate final @android.annotation.Nullable android.os.Bundle mClientState\nprivate final @android.service.autofill.FillRequest.RequestFlags int mFlags\nprivate final @android.annotation.Nullable android.view.inputmethod.InlineSuggestionsRequest mInlineSuggestionsRequest\nprivate final @android.annotation.Nullable android.content.IntentSender mDelayedFillIntentSender\nprivate void onConstructed()\nclass FillRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstructor=true, genHiddenConstDefs=true)") + inputSignatures = "public static final @android.service.autofill.FillRequest.RequestFlags int FLAG_MANUAL_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_PASSWORD_INPUT_TYPE\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_VIEW_NOT_FOCUSED\npublic static final @android.annotation.FlaggedApi @java.lang.Deprecated @android.service.autofill.FillRequest.RequestFlags int FLAG_SUPPORTS_FILL_DIALOG\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_IME_SHOWING\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_RESET_FILL_DIALOG_STATE\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_PCC_DETECTION\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_SCREEN_HAS_CREDMAN_FIELD\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_VIEW_REQUESTS_CREDMAN_SERVICE\npublic static final int INVALID_REQUEST_ID\nprivate final int mId\nprivate final @android.annotation.NonNull java.util.List<android.service.autofill.FillContext> mFillContexts\nprivate final @android.annotation.NonNull java.util.List<java.lang.String> mHints\nprivate final @android.annotation.Nullable android.os.Bundle mClientState\nprivate final @android.service.autofill.FillRequest.RequestFlags int mFlags\nprivate final @android.annotation.Nullable android.view.inputmethod.InlineSuggestionsRequest mInlineSuggestionsRequest\nprivate final @android.annotation.Nullable android.content.IntentSender mDelayedFillIntentSender\nprivate void onConstructed()\nclass FillRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstructor=true, genHiddenConstDefs=true)") @Deprecated private void __metadata() {} diff --git a/core/java/android/service/dreams/flags.aconfig b/core/java/android/service/dreams/flags.aconfig index 72f2de805474..dfc11dcb5427 100644 --- a/core/java/android/service/dreams/flags.aconfig +++ b/core/java/android/service/dreams/flags.aconfig @@ -67,3 +67,13 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "cleanup_dream_settings_on_uninstall" + namespace: "systemui" + description: "Cleans up dream settings if dream package is uninstalled." + bug: "338210427" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/core/java/android/service/quickaccesswallet/flags.aconfig b/core/java/android/service/quickaccesswallet/flags.aconfig new file mode 100644 index 000000000000..07311d5ffbe1 --- /dev/null +++ b/core/java/android/service/quickaccesswallet/flags.aconfig @@ -0,0 +1,9 @@ +package: "android.service.quickaccesswallet" +container: "system" + +flag { + name: "launch_wallet_option_on_power_double_tap" + namespace: "wallet_integrations" + description: "Option to launch the Wallet app on double-tap of the power button" + bug: "378469025" +}
\ No newline at end of file diff --git a/core/java/android/service/settings/preferences/GetValueRequest.aidl b/core/java/android/service/settings/preferences/GetValueRequest.aidl new file mode 100644 index 000000000000..2a0eb09aa2a4 --- /dev/null +++ b/core/java/android/service/settings/preferences/GetValueRequest.aidl @@ -0,0 +1,4 @@ +package android.service.settings.preferences; + +/** @hide */ +parcelable GetValueRequest;
\ No newline at end of file diff --git a/core/java/android/service/settings/preferences/GetValueRequest.java b/core/java/android/service/settings/preferences/GetValueRequest.java new file mode 100644 index 000000000000..4f82800d1855 --- /dev/null +++ b/core/java/android/service/settings/preferences/GetValueRequest.java @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.service.settings.preferences; + +import android.annotation.FlaggedApi; +import android.os.Parcel; +import android.os.Parcelable; +import android.text.TextUtils; + +import androidx.annotation.NonNull; + +import com.android.settingslib.flags.Flags; + +import java.util.Objects; + +/** + * Request parameters to retrieve the current value of a Settings Preference. + * + * <p>This object passed to {@link SettingsPreferenceService#onGetPreferenceValue} will result + * in a {@link GetValueResult}. + * + * <ul> + * <li>{@link #getScreenKey} is a parameter to distinguish the container screen + * of a preference as a preference key may not be unique within its application. + * <li>{@link #getPreferenceKey} is a parameter to identify the preference for which the value is + * being requested. These keys will be unique with their Preference Screen, but may not be unique + * within their application, so it is required to pair this with {@link #getScreenKey} to + * ensure this request matches the intended target. + * </ul> + */ +@FlaggedApi(Flags.FLAG_SETTINGS_CATALYST) +public final class GetValueRequest implements Parcelable { + + @NonNull + private final String mScreenKey; + @NonNull + private final String mPreferenceKey; + + /** + * Returns the screen key of requested Preference. + */ + @NonNull + public String getScreenKey() { + return mScreenKey; + } + + /** + * Returns the key of requested Preference. + */ + @NonNull + public String getPreferenceKey() { + return mPreferenceKey; + } + + private GetValueRequest(@NonNull Builder builder) { + mScreenKey = builder.mScreenKey; + mPreferenceKey = builder.mPreferenceKey; + } + + private GetValueRequest(@NonNull Parcel in) { + mScreenKey = Objects.requireNonNull(in.readString8()); + mPreferenceKey = Objects.requireNonNull(in.readString8()); + } + + /** @hide */ + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeString8(mScreenKey); + dest.writeString8(mPreferenceKey); + } + + /** @hide */ + @Override + public int describeContents() { + return 0; + } + + /** + * Parcelable Creator for {@link GetValueRequest}. + */ + @NonNull + public static final Creator<GetValueRequest> CREATOR = new Creator<GetValueRequest>() { + @Override + public GetValueRequest createFromParcel(@NonNull Parcel in) { + return new GetValueRequest(in); + } + + @Override + public GetValueRequest[] newArray(int size) { + return new GetValueRequest[size]; + } + }; + + /** + * Builder to construct {@link GetValueRequest}. + */ + public static final class Builder { + private final String mScreenKey; + private final String mPreferenceKey; + + /** + * Create Builder instance. + * @param screenKey required to be not empty + * @param preferenceKey required to be not empty + */ + public Builder(@NonNull String screenKey, @NonNull String preferenceKey) { + if (TextUtils.isEmpty(screenKey)) { + throw new IllegalArgumentException("screenKey cannot be empty"); + } + if (TextUtils.isEmpty(preferenceKey)) { + throw new IllegalArgumentException("preferenceKey cannot be empty"); + } + mScreenKey = screenKey; + mPreferenceKey = preferenceKey; + } + + /** + * Constructs an immutable {@link GetValueRequest} object. + */ + @NonNull + public GetValueRequest build() { + return new GetValueRequest(this); + } + } +} diff --git a/core/java/android/service/settings/preferences/GetValueResult.aidl b/core/java/android/service/settings/preferences/GetValueResult.aidl new file mode 100644 index 000000000000..b5ebd35a3a37 --- /dev/null +++ b/core/java/android/service/settings/preferences/GetValueResult.aidl @@ -0,0 +1,4 @@ +package android.service.settings.preferences; + +/** @hide */ +parcelable GetValueResult;
\ No newline at end of file diff --git a/core/java/android/service/settings/preferences/GetValueResult.java b/core/java/android/service/settings/preferences/GetValueResult.java new file mode 100644 index 000000000000..369dea77cc85 --- /dev/null +++ b/core/java/android/service/settings/preferences/GetValueResult.java @@ -0,0 +1,213 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.settings.preferences; + +import android.annotation.FlaggedApi; +import android.annotation.IntDef; +import android.os.Parcel; +import android.os.Parcelable; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.settingslib.flags.Flags; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Result object given a corresponding {@link GetValueRequest}. + * <ul> + * <li>If the request was successful, {@link #getResultCode} will be {@link #RESULT_OK}, + * {@link #getValue} will be populated with the settings preference value and + * {@link #getMetadata} will be populated with its metadata. + * <li>If the request is unsuccessful, {@link #getResultCode} be a value other than + * {@link #RESULT_OK} - see documentation for those possibilities to understand the cause + * of the failure. + * </ul> + */ +@FlaggedApi(Flags.FLAG_SETTINGS_CATALYST) +public final class GetValueResult implements Parcelable { + + @ResultCode + private final int mResultCode; + @Nullable + private final SettingsPreferenceValue mValue; + @Nullable + private final SettingsPreferenceMetadata mMetadata; + + /** + * Returns the result code indicating status of the request. + */ + @ResultCode + public int getResultCode() { + return mResultCode; + } + + /** + * Returns the value of requested Preference if request successful. + */ + @Nullable + public SettingsPreferenceValue getValue() { + return mValue; + } + + /** + * Returns the metadata of requested Preference if request successful. + */ + @Nullable + public SettingsPreferenceMetadata getMetadata() { + return mMetadata; + } + + /** @hide */ + @IntDef(prefix = { "RESULT_" }, value = { + RESULT_OK, + RESULT_UNSUPPORTED, + RESULT_UNAVAILABLE, + RESULT_REQUIRE_APP_PERMISSION, + RESULT_DISALLOW, + RESULT_INVALID_REQUEST, + RESULT_INTERNAL_ERROR, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ResultCode { + } + + /** Request is successful. */ + public static final int RESULT_OK = 0; + /** + * Requested preference is not supported by this API. + * <p>Retry not advised. + */ + public static final int RESULT_UNSUPPORTED = 1; + /** + * Preference is currently not available, likely due to device state or the state of + * a dependency. + * <p>Retry may succeed if underlying conditions change. + */ + public static final int RESULT_UNAVAILABLE = 2; + /** + * Requested preference requires permissions not held by the calling application. + * <p>Retry may succeed if necessary permissions are obtained. + */ + public static final int RESULT_REQUIRE_APP_PERMISSION = 3; + /** + * Requested preference is not allowed for access in this API under the current device policy. + * <p>Retry may succeed if underlying conditions change. + */ + public static final int RESULT_DISALLOW = 4; + /** + * Request object is not valid. + * <p>Retry not advised with current parameters. + */ + public static final int RESULT_INVALID_REQUEST = 5; + /** + * API call failed due to an issue with the service binding. + * <p>Retry may succeed. + */ + public static final int RESULT_INTERNAL_ERROR = 6; + + + private GetValueResult(@NonNull Builder builder) { + mResultCode = builder.mResultCode; + mValue = builder.mValue; + mMetadata = builder.mMetadata; + } + + private GetValueResult(@NonNull Parcel in) { + mResultCode = in.readInt(); + mValue = in.readParcelable(SettingsPreferenceValue.class.getClassLoader(), + SettingsPreferenceValue.class); + mMetadata = in.readParcelable(SettingsPreferenceMetadata.class.getClassLoader(), + SettingsPreferenceMetadata.class); + } + + /** @hide */ + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mResultCode); + dest.writeParcelable(mValue, flags); + dest.writeParcelable(mMetadata, flags); + } + + /** @hide */ + @Override + public int describeContents() { + return 0; + } + + /** + * Parcelable Creator for {@link GetValueResult}. + */ + @NonNull + public static final Creator<GetValueResult> CREATOR = new Creator<>() { + @Override + public GetValueResult createFromParcel(@NonNull Parcel in) { + return new GetValueResult(in); + } + + @Override + public GetValueResult[] newArray(int size) { + return new GetValueResult[size]; + } + }; + + /** + * Builder to construct {@link GetValueResult}. + */ + public static final class Builder { + @ResultCode + private final int mResultCode; + private SettingsPreferenceValue mValue; + private SettingsPreferenceMetadata mMetadata; + + /** + * Create Builder instance. + * @param resultCode indicates status of the request + */ + public Builder(@ResultCode int resultCode) { + mResultCode = resultCode; + } + + /** + * Sets the preference value on the result. + */ + @NonNull + public Builder setValue(@Nullable SettingsPreferenceValue value) { + mValue = value; + return this; + } + + /** + * Sets the metadata on the result. + */ + @NonNull + public Builder setMetadata(@Nullable SettingsPreferenceMetadata metadata) { + mMetadata = metadata; + return this; + } + + /** + * Constructs an immutable {@link GetValueResult} object. + */ + @NonNull + public GetValueResult build() { + return new GetValueResult(this); + } + } +} diff --git a/core/java/android/service/settings/preferences/IGetValueCallback.aidl b/core/java/android/service/settings/preferences/IGetValueCallback.aidl new file mode 100644 index 000000000000..bbc7423f453e --- /dev/null +++ b/core/java/android/service/settings/preferences/IGetValueCallback.aidl @@ -0,0 +1,9 @@ +package android.service.settings.preferences; + +import android.service.settings.preferences.GetValueResult; + +/** @hide */ +oneway interface IGetValueCallback { + void onSuccess(in GetValueResult result) = 1; + void onFailure() = 2; +} diff --git a/core/java/android/service/settings/preferences/IMetadataCallback.aidl b/core/java/android/service/settings/preferences/IMetadataCallback.aidl new file mode 100644 index 000000000000..3bd5ebe93660 --- /dev/null +++ b/core/java/android/service/settings/preferences/IMetadataCallback.aidl @@ -0,0 +1,9 @@ +package android.service.settings.preferences; + +import android.service.settings.preferences.MetadataResult; + +/** @hide */ +oneway interface IMetadataCallback { + void onSuccess(in MetadataResult result); + void onFailure(); +} diff --git a/core/java/android/service/settings/preferences/ISetValueCallback.aidl b/core/java/android/service/settings/preferences/ISetValueCallback.aidl new file mode 100644 index 000000000000..0765660c83c3 --- /dev/null +++ b/core/java/android/service/settings/preferences/ISetValueCallback.aidl @@ -0,0 +1,9 @@ +package android.service.settings.preferences; + +import android.service.settings.preferences.SetValueResult; + +/** @hide */ +oneway interface ISetValueCallback { + void onSuccess(in SetValueResult result); + void onFailure(); +} diff --git a/core/java/android/service/settings/preferences/ISettingsPreferenceService.aidl b/core/java/android/service/settings/preferences/ISettingsPreferenceService.aidl new file mode 100644 index 000000000000..64a8b90fe581 --- /dev/null +++ b/core/java/android/service/settings/preferences/ISettingsPreferenceService.aidl @@ -0,0 +1,18 @@ +package android.service.settings.preferences; + +import android.service.settings.preferences.GetValueRequest; +import android.service.settings.preferences.IGetValueCallback; +import android.service.settings.preferences.IMetadataCallback; +import android.service.settings.preferences.ISetValueCallback; +import android.service.settings.preferences.MetadataRequest; +import android.service.settings.preferences.SetValueRequest; + +/** @hide */ +oneway interface ISettingsPreferenceService { + @EnforcePermission("READ_SYSTEM_PREFERENCES") + void getAllPreferenceMetadata(in MetadataRequest request, IMetadataCallback callback) = 1; + @EnforcePermission("READ_SYSTEM_PREFERENCES") + void getPreferenceValue(in GetValueRequest request, IGetValueCallback callback) = 2; + @EnforcePermission(allOf = {"READ_SYSTEM_PREFERENCES", "WRITE_SYSTEM_PREFERENCES"}) + void setPreferenceValue(in SetValueRequest request, ISetValueCallback callback) = 3; +} diff --git a/core/java/android/service/settings/preferences/MetadataRequest.aidl b/core/java/android/service/settings/preferences/MetadataRequest.aidl new file mode 100644 index 000000000000..dc3cbc42661e --- /dev/null +++ b/core/java/android/service/settings/preferences/MetadataRequest.aidl @@ -0,0 +1,4 @@ +package android.service.settings.preferences; + +/** @hide */ +parcelable MetadataRequest;
\ No newline at end of file diff --git a/core/java/android/service/settings/preferences/MetadataRequest.java b/core/java/android/service/settings/preferences/MetadataRequest.java new file mode 100644 index 000000000000..ffecc6bec5b2 --- /dev/null +++ b/core/java/android/service/settings/preferences/MetadataRequest.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.settings.preferences; + +import android.annotation.FlaggedApi; +import android.os.Parcel; +import android.os.Parcelable; + +import androidx.annotation.NonNull; + +import com.android.settingslib.flags.Flags; + +/** + * Request parameters to retrieve all metadata for all available settings preferences within this + * application. + * + * <p>This object passed to {@link SettingsPreferenceService#onGetAllPreferenceMetadata} will result + * in a {@link MetadataResult}. + */ +@FlaggedApi(Flags.FLAG_SETTINGS_CATALYST) +public final class MetadataRequest implements Parcelable { + private MetadataRequest() {} + + /** @hide */ + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + } + + /** @hide */ + @Override + public int describeContents() { + return 0; + } + + /** + * Parcelable Creator for {@link MetadataRequest}. + */ + @NonNull + public static final Creator<MetadataRequest> CREATOR = new Creator<>() { + @Override + public MetadataRequest createFromParcel(@NonNull Parcel in) { + return new MetadataRequest(); + } + + @Override + public MetadataRequest[] newArray(int size) { + return new MetadataRequest[size]; + } + }; + + /** + * Builder to construct {@link MetadataRequest}. + */ + public static final class Builder { + /** Constructs an immutable {@link MetadataRequest} object. */ + @NonNull + public MetadataRequest build() { + return new MetadataRequest(); + } + } +} diff --git a/core/java/android/service/settings/preferences/MetadataResult.aidl b/core/java/android/service/settings/preferences/MetadataResult.aidl new file mode 100644 index 000000000000..af9e8a86e3ab --- /dev/null +++ b/core/java/android/service/settings/preferences/MetadataResult.aidl @@ -0,0 +1,4 @@ +package android.service.settings.preferences; + +/** @hide */ +parcelable MetadataResult;
\ No newline at end of file diff --git a/core/java/android/service/settings/preferences/MetadataResult.java b/core/java/android/service/settings/preferences/MetadataResult.java new file mode 100644 index 000000000000..6a65dcc9c757 --- /dev/null +++ b/core/java/android/service/settings/preferences/MetadataResult.java @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.service.settings.preferences; + +import android.annotation.FlaggedApi; +import android.annotation.IntDef; +import android.os.Parcel; +import android.os.Parcelable; + +import androidx.annotation.NonNull; + +import com.android.settingslib.flags.Flags; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Result object given a corresponding {@link MetadataRequest}. + * <ul> + * <li>If the request was successful, {@link #getResultCode} will be {@link #RESULT_OK} and + * {@link #getMetadataList} will be populated with metadata for all available preferences within + * this application. + * <li>If the request is unsuccessful, {@link #getResultCode} be a value other than + * {@link #RESULT_OK} - see documentation for those possibilities to understand the cause + * of the failure. + * </ul> + */ +@FlaggedApi(Flags.FLAG_SETTINGS_CATALYST) +public final class MetadataResult implements Parcelable { + + @ResultCode + private final int mResultCode; + @NonNull + private final List<SettingsPreferenceMetadata> mMetadataList; + + /** + * Returns the result code indicating status of the request. + */ + @ResultCode + public int getResultCode() { + return mResultCode; + } + + /** + * Returns the list of available Preference Metadata. + * <p>This instance is shared so this list should not be modified. + */ + @NonNull + public List<SettingsPreferenceMetadata> getMetadataList() { + return mMetadataList; + } + + /** @hide */ + @IntDef(prefix = { "RESULT_" }, value = { + RESULT_OK, + RESULT_UNSUPPORTED, + RESULT_INTERNAL_ERROR + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ResultCode { + } + + /** Request is successful. */ + public static final int RESULT_OK = 0; + /** + * No preferences in this application support this API. + * <p>Retry not advised. + */ + public static final int RESULT_UNSUPPORTED = 1; + /** + * API call failed due to an issue with the service binding. + * <p>Retry may succeed. + */ + public static final int RESULT_INTERNAL_ERROR = 2; + + private MetadataResult(@NonNull Builder builder) { + mResultCode = builder.mResultCode; + mMetadataList = builder.mMetadataList; + } + private MetadataResult(@NonNull Parcel in) { + mResultCode = in.readInt(); + mMetadataList = new ArrayList<>(); + in.readTypedList(mMetadataList, SettingsPreferenceMetadata.CREATOR); + } + + /** @hide */ + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mResultCode); + dest.writeTypedList(mMetadataList, flags); + } + + /** @hide */ + @Override + public int describeContents() { + return 0; + } + + /** + * Parcelable Creator for {@link MetadataResult}. + */ + @NonNull + public static final Creator<MetadataResult> CREATOR = new Creator<>() { + @Override + public MetadataResult createFromParcel(@NonNull Parcel in) { + return new MetadataResult(in); + } + + @Override + public MetadataResult[] newArray(int size) { + return new MetadataResult[size]; + } + }; + + /** + * Builder to construct {@link MetadataResult}. + */ + public static final class Builder { + @ResultCode + private final int mResultCode; + private List<SettingsPreferenceMetadata> mMetadataList = Collections.emptyList(); + + /** + * Create Builder instance. + * @param resultCode indicates status of the request + */ + public Builder(@ResultCode int resultCode) { + mResultCode = resultCode; + } + + /** + * Sets the metadata list on the result. + */ + @NonNull + public Builder setMetadataList(@NonNull List<SettingsPreferenceMetadata> metadataList) { + mMetadataList = metadataList; + return this; + } + + /** + * Constructs an immutable {@link MetadataResult} object. + */ + @NonNull + public MetadataResult build() { + return new MetadataResult(this); + } + } +} diff --git a/core/java/android/service/settings/preferences/SetValueRequest.aidl b/core/java/android/service/settings/preferences/SetValueRequest.aidl new file mode 100644 index 000000000000..198e333d5cb6 --- /dev/null +++ b/core/java/android/service/settings/preferences/SetValueRequest.aidl @@ -0,0 +1,4 @@ +package android.service.settings.preferences; + +/** @hide */ +parcelable SetValueRequest;
\ No newline at end of file diff --git a/core/java/android/service/settings/preferences/SetValueRequest.java b/core/java/android/service/settings/preferences/SetValueRequest.java new file mode 100644 index 000000000000..f7600aecdfaf --- /dev/null +++ b/core/java/android/service/settings/preferences/SetValueRequest.java @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.service.settings.preferences; + +import android.annotation.FlaggedApi; +import android.os.Parcel; +import android.os.Parcelable; +import android.text.TextUtils; + +import androidx.annotation.NonNull; + +import com.android.settingslib.flags.Flags; + +import java.util.Objects; + +/** + * Request parameters to set the current value to a Settings Preference. + * <p>This object passed to {@link SettingsPreferenceService#onSetPreferenceValue} will result in a + * {@link SetValueResult}. + * <ul> + * <li>{@link #getScreenKey} is a parameter to distinguish the container screen + * of a preference as a preference key may not be unique within its application. + * <li>{@link #getPreferenceKey} is a parameter to identify the preference for which the value is + * being requested. These keys will be unique with their Preference Screen, but may not be unique + * within their application, so it is required to pair this with {@link #getScreenKey} to + * ensure this request matches the intended target. + * <li>{@link #getPreferenceValue} is a parameter to specify the value that this request aims to + * set. If this value is invalid (malformed or does not match the type of the preference) then + * this request will fail. + * </ul> + */ +@FlaggedApi(Flags.FLAG_SETTINGS_CATALYST) +public final class SetValueRequest implements Parcelable { + + @NonNull + private final String mScreenKey; + @NonNull + private final String mPreferenceKey; + @NonNull + private final SettingsPreferenceValue mPreferenceValue; + + /** + * Returns the screen key of requested Preference. + */ + @NonNull + public String getScreenKey() { + return mScreenKey; + } + + /** + * Returns the key of requested Preference. + */ + @NonNull + public String getPreferenceKey() { + return mPreferenceKey; + } + + /** + * Returns the value of requested Preference. + */ + @NonNull + public SettingsPreferenceValue getPreferenceValue() { + return mPreferenceValue; + } + + private SetValueRequest(@NonNull Builder builder) { + mScreenKey = builder.mScreenKey; + mPreferenceKey = builder.mPreferenceKey; + mPreferenceValue = builder.mPreferenceValue; + } + + private SetValueRequest(@NonNull Parcel in) { + mScreenKey = Objects.requireNonNull(in.readString8()); + mPreferenceKey = Objects.requireNonNull(in.readString8()); + mPreferenceValue = Objects.requireNonNull(in.readParcelable( + SettingsPreferenceValue.class.getClassLoader(), SettingsPreferenceValue.class)); + } + + /** @hide */ + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeString8(mScreenKey); + dest.writeString8(mPreferenceKey); + dest.writeParcelable(mPreferenceValue, flags); + } + + /** @hide */ + @Override + public int describeContents() { + return 0; + } + + /** + * Parcelable Creator for {@link SetValueRequest}. + */ + @NonNull + public static final Creator<SetValueRequest> CREATOR = new Creator<SetValueRequest>() { + @Override + public SetValueRequest createFromParcel(@NonNull Parcel in) { + return new SetValueRequest(in); + } + + @Override + public SetValueRequest[] newArray(int size) { + return new SetValueRequest[size]; + } + }; + + /** + * Builder to construct {@link SetValueRequest}. + */ + public static final class Builder { + private final String mScreenKey; + private final String mPreferenceKey; + private final SettingsPreferenceValue mPreferenceValue; + + /** + * Create Builder instance. + * @param screenKey required to be not empty + * @param preferenceKey required to be not empty + * @param value value to set to requested Preference + */ + public Builder(@NonNull String screenKey, @NonNull String preferenceKey, + @NonNull SettingsPreferenceValue value) { + if (TextUtils.isEmpty(screenKey)) { + throw new IllegalArgumentException("screenKey cannot be empty"); + } + if (TextUtils.isEmpty(preferenceKey)) { + throw new IllegalArgumentException("preferenceKey cannot be empty"); + } + mScreenKey = screenKey; + mPreferenceKey = preferenceKey; + mPreferenceValue = value; + } + + /** + * Constructs an immutable {@link SetValueRequest} object. + */ + @NonNull + public SetValueRequest build() { + return new SetValueRequest(this); + } + } +} diff --git a/core/java/android/service/settings/preferences/SetValueResult.aidl b/core/java/android/service/settings/preferences/SetValueResult.aidl new file mode 100644 index 000000000000..f54813484d68 --- /dev/null +++ b/core/java/android/service/settings/preferences/SetValueResult.aidl @@ -0,0 +1,4 @@ +package android.service.settings.preferences; + +/** @hide */ +parcelable SetValueResult;
\ No newline at end of file diff --git a/core/java/android/service/settings/preferences/SetValueResult.java b/core/java/android/service/settings/preferences/SetValueResult.java new file mode 100644 index 000000000000..cb1776abd3bc --- /dev/null +++ b/core/java/android/service/settings/preferences/SetValueResult.java @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.service.settings.preferences; + +import android.annotation.FlaggedApi; +import android.annotation.IntDef; +import android.os.Parcel; +import android.os.Parcelable; + +import androidx.annotation.NonNull; + +import com.android.settingslib.flags.Flags; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Result object given a corresponding {@link SetValueRequest}. + * <ul> + * <li>If the request was successful, {@link #getResultCode} will be {@link #RESULT_OK}. + * <li>If the request is unsuccessful, {@link #getResultCode} be a value other than + * {@link #RESULT_OK} - see documentation for those possibilities to understand the cause + * of the failure. + * </ul> + */ +@FlaggedApi(Flags.FLAG_SETTINGS_CATALYST) +public final class SetValueResult implements Parcelable { + + @ResultCode + private final int mResultCode; + + /** + * Returns the result code indicating status of the request. + */ + @ResultCode + public int getResultCode() { + return mResultCode; + } + + /** @hide */ + @IntDef(prefix = { "RESULT_" }, value = { + RESULT_OK, + RESULT_UNSUPPORTED, + RESULT_DISABLED, + RESULT_RESTRICTED, + RESULT_UNAVAILABLE, + RESULT_REQUIRE_APP_PERMISSION, + RESULT_REQUIRE_USER_CONSENT, + RESULT_DISALLOW, + RESULT_INVALID_REQUEST, + RESULT_INTERNAL_ERROR + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ResultCode { + } + + /** Request is successful and the value was set. */ + public static final int RESULT_OK = 0; + /** + * Requested preference is not supported by this API. + * <p>Retry not advised. + */ + public static final int RESULT_UNSUPPORTED = 1; + /** + * Requested preference is disabled, thus unable to be set in this state. + * <p>Retry may succeed if underlying conditions change. + */ + public static final int RESULT_DISABLED = 2; + /** + * Requested preference is restricted, thus unable to be set under this policy. + * <p>Retry may succeed if underlying conditions change. + */ + public static final int RESULT_RESTRICTED = 3; + /** + * Preference is currently not available, likely due to device state or the state of + * a dependency. + * <p>Retry may succeed if underlying conditions change. + */ + public static final int RESULT_UNAVAILABLE = 4; + /** + * Requested preference requires permissions not held by the calling application. + * <p>Retry may succeed if necessary permissions are obtained. + */ + public static final int RESULT_REQUIRE_APP_PERMISSION = 5; + /** + * User consent was not approved for this operation. + * <p>Retry may succeed if user provides consent. + */ + public static final int RESULT_REQUIRE_USER_CONSENT = 6; + /** + * Requested preference is not allowed for access in this API under the current device policy. + * <p>Retry may succeed if underlying conditions change. + */ + public static final int RESULT_DISALLOW = 7; + /** + * Request object is not valid. + * <p>Retry not advised with current parameters. + */ + public static final int RESULT_INVALID_REQUEST = 8; + /** + * API call failed due to an issue with the service binding. + * <p>Retry may succeed. + */ + public static final int RESULT_INTERNAL_ERROR = 9; + + private SetValueResult(@NonNull Builder builder) { + mResultCode = builder.mResultCode; + } + + private SetValueResult(@NonNull Parcel in) { + mResultCode = in.readInt(); + } + + /** @hide */ + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mResultCode); + } + + /** @hide */ + @Override + public int describeContents() { + return 0; + } + + /** + * Parcelable Creator for {@link SetValueResult}. + */ + @NonNull + public static final Creator<SetValueResult> CREATOR = new Creator<>() { + @Override + public SetValueResult createFromParcel(@NonNull Parcel in) { + return new SetValueResult(in); + } + + @Override + public SetValueResult[] newArray(int size) { + return new SetValueResult[size]; + } + }; + + /** + * Builder to construct {@link SetValueResult}. + */ + public static final class Builder { + @ResultCode + private final int mResultCode; + + /** + * Create Builder instance. + * @param resultCode indicates status of the request + */ + public Builder(@ResultCode int resultCode) { + mResultCode = resultCode; + } + + /** + * Constructs an immutable {@link SetValueResult} object. + */ + @NonNull + public SetValueResult build() { + return new SetValueResult(this); + } + } +} diff --git a/core/java/android/service/settings/preferences/SettingsPreferenceMetadata.java b/core/java/android/service/settings/preferences/SettingsPreferenceMetadata.java new file mode 100644 index 000000000000..1d08c5217129 --- /dev/null +++ b/core/java/android/service/settings/preferences/SettingsPreferenceMetadata.java @@ -0,0 +1,436 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.service.settings.preferences; + +import android.annotation.FlaggedApi; +import android.annotation.IntDef; +import android.annotation.SuppressLint; +import android.app.PendingIntent; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; +import android.text.TextUtils; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.settingslib.flags.Flags; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +/** + * Data object representation of a Settings Preference definition and state. + */ +@FlaggedApi(Flags.FLAG_SETTINGS_CATALYST) +public final class SettingsPreferenceMetadata implements Parcelable { + + @NonNull + private final String mKey; + @NonNull + private final String mScreenKey; + @Nullable + private final String mTitle; + @Nullable + private final String mSummary; + @NonNull + private final List<String> mBreadcrumbs; + @NonNull + private final List<String> mReadPermissions; + @NonNull + private final List<String> mWritePermissions; + private final boolean mEnabled; + private final boolean mAvailable; + private final boolean mWritable; + private final boolean mRestricted; + private final int mSensitivity; + @Nullable + private final PendingIntent mLaunchIntent; + @NonNull + private final Bundle mExtras; + + /** + * Returns the key of Preference. + */ + @NonNull + public String getKey() { + return mKey; + } + + /** + * Returns the screen key of Preference. + */ + @NonNull + public String getScreenKey() { + return mScreenKey; + } + + /** + * Returns the title of Preference. + */ + @Nullable + public String getTitle() { + return mTitle; + } + + /** + * Returns the summary of Preference. + */ + @Nullable + public String getSummary() { + return mSummary; + } + + /** + * Returns the breadcrumbs (navigation context) of Preference. + * <p>May be empty. + */ + @NonNull + public List<String> getBreadcrumbs() { + return mBreadcrumbs; + } + + /** + * Returns the permissions required to read this Preference's value. + * <p>May be empty. + */ + @NonNull + public List<String> getReadPermissions() { + return mReadPermissions; + } + + /** + * Returns the permissions required to write this Preference's value. + * <p>May be empty. + */ + @NonNull + public List<String> getWritePermissions() { + return mWritePermissions; + } + + /** + * Returns whether Preference is enabled. + */ + public boolean isEnabled() { + return mEnabled; + } + + /** + * Returns whether Preference is available. + */ + public boolean isAvailable() { + return mAvailable; + } + + /** + * Returns whether Preference is writable. + */ + public boolean isWritable() { + return mWritable; + } + + /** + * Returns whether Preference is restricted. + */ + public boolean isRestricted() { + return mRestricted; + } + + /** + * Returns the write-level sensitivity of Preference. + */ + @WriteSensitivity + public int getWriteSensitivity() { + return mSensitivity; + } + + /** + * Returns the intent to launch the host app page for this Preference. + */ + @Nullable + public PendingIntent getLaunchIntent() { + return mLaunchIntent; + } + + /** + * Returns any additional fields specific to this preference. + * <p>Treat all data as optional. + */ + @NonNull + public Bundle getExtras() { + return mExtras; + } + + /** @hide */ + @IntDef(value = { + NOT_SENSITIVE, + SENSITIVE, + INTENT_ONLY + }) + @Retention(RetentionPolicy.SOURCE) + public @interface WriteSensitivity {} + + /** + * Preference is not sensitive, thus its value is writable without explicit consent, assuming + * all necessary permissions are granted. + */ + public static final int NOT_SENSITIVE = 0; + /** + * Preference is sensitive, meaning that in addition to necessary permissions, writing its value + * will also request explicit user consent. + */ + public static final int SENSITIVE = 1; + /** + * Preference is not permitted for write-access via API and must be changed via Settings page. + */ + public static final int INTENT_ONLY = 2; + + private SettingsPreferenceMetadata(@NonNull Builder builder) { + mKey = builder.mKey; + mScreenKey = builder.mScreenKey; + mTitle = builder.mTitle; + mSummary = builder.mSummary; + mBreadcrumbs = builder.mBreadcrumbs; + mReadPermissions = builder.mReadPermissions; + mWritePermissions = builder.mWritePermissions; + mEnabled = builder.mEnabled; + mAvailable = builder.mAvailable; + mWritable = builder.mWritable; + mRestricted = builder.mRestricted; + mSensitivity = builder.mSensitivity; + mLaunchIntent = builder.mLaunchIntent; + mExtras = Objects.requireNonNullElseGet(builder.mExtras, Bundle::new); + } + @SuppressLint("ParcelClassLoader") + private SettingsPreferenceMetadata(@NonNull Parcel in) { + mKey = Objects.requireNonNull(in.readString8()); + mScreenKey = Objects.requireNonNull(in.readString8()); + mTitle = in.readString8(); + mSummary = in.readString8(); + mBreadcrumbs = new ArrayList<>(); + in.readStringList(mBreadcrumbs); + mReadPermissions = new ArrayList<>(); + in.readStringList(mReadPermissions); + mWritePermissions = new ArrayList<>(); + in.readStringList(mWritePermissions); + mEnabled = in.readBoolean(); + mAvailable = in.readBoolean(); + mWritable = in.readBoolean(); + mRestricted = in.readBoolean(); + mSensitivity = in.readInt(); + mLaunchIntent = in.readParcelable(PendingIntent.class.getClassLoader(), + PendingIntent.class); + mExtras = Objects.requireNonNullElseGet(in.readBundle(), Bundle::new); + } + + /** @hide */ + @Override + public int describeContents() { + return 0; + } + + /** @hide */ + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeString8(mKey); + dest.writeString8(mScreenKey); + dest.writeString8(mTitle); + dest.writeString8(mSummary); + dest.writeStringList(mBreadcrumbs); + dest.writeStringList(mReadPermissions); + dest.writeStringList(mWritePermissions); + dest.writeBoolean(mEnabled); + dest.writeBoolean(mAvailable); + dest.writeBoolean(mWritable); + dest.writeBoolean(mRestricted); + dest.writeInt(mSensitivity); + dest.writeParcelable(mLaunchIntent, flags); + dest.writeBundle(mExtras); + } + + /** + * Parcelable Creator for {@link SettingsPreferenceMetadata}. + */ + @NonNull + public static final Creator<SettingsPreferenceMetadata> CREATOR = new Creator<>() { + @Override + public SettingsPreferenceMetadata createFromParcel(@NonNull Parcel in) { + return new SettingsPreferenceMetadata(in); + } + + @Override + public SettingsPreferenceMetadata[] newArray(int size) { + return new SettingsPreferenceMetadata[size]; + } + }; + + /** + * Builder to construct {@link SettingsPreferenceMetadata}. + */ + public static final class Builder { + private final String mScreenKey; + private final String mKey; + private String mTitle; + private String mSummary; + private List<String> mBreadcrumbs = Collections.emptyList(); + private List<String> mReadPermissions = Collections.emptyList(); + private List<String> mWritePermissions = Collections.emptyList(); + private boolean mEnabled = false; + private boolean mAvailable = false; + private boolean mWritable = false; + private boolean mRestricted = false; + @WriteSensitivity private int mSensitivity = INTENT_ONLY; + private PendingIntent mLaunchIntent; + private Bundle mExtras; + + /** + * Create Builder instance. + * @param screenKey required to be not empty + * @param key required to be not empty + */ + public Builder(@NonNull String screenKey, @NonNull String key) { + if (TextUtils.isEmpty(screenKey)) { + throw new IllegalArgumentException("screenKey cannot be empty"); + } + if (TextUtils.isEmpty(key)) { + throw new IllegalArgumentException("key cannot be empty"); + } + mScreenKey = screenKey; + mKey = key; + } + + /** + * Sets the preference title. + */ + @NonNull + public Builder setTitle(@Nullable String title) { + mTitle = title; + return this; + } + + /** + * Sets the preference summary. + */ + @NonNull + public Builder setSummary(@Nullable String summary) { + mSummary = summary; + return this; + } + + /** + * Sets the preference breadcrumbs (navigation context). + */ + @NonNull + public Builder setBreadcrumbs(@NonNull List<String> breadcrumbs) { + mBreadcrumbs = breadcrumbs; + return this; + } + + /** + * Sets the permissions required for reading this preference. + */ + @NonNull + public Builder setReadPermissions(@NonNull List<String> readPermissions) { + mReadPermissions = readPermissions; + return this; + } + + /** + * Sets the permissions required for writing this preference. + */ + @NonNull + public Builder setWritePermissions(@NonNull List<String> writePermissions) { + mWritePermissions = writePermissions; + return this; + } + + /** + * Set whether the preference is enabled. + */ + @NonNull + public Builder setEnabled(boolean enabled) { + mEnabled = enabled; + return this; + } + + /** + * Sets whether the preference is available. + */ + @NonNull + public Builder setAvailable(boolean available) { + mAvailable = available; + return this; + } + + /** + * Sets whether the preference is writable. + */ + @NonNull + public Builder setWritable(boolean writable) { + mWritable = writable; + return this; + } + + /** + * Sets whether the preference is restricted. + */ + @NonNull + public Builder setRestricted(boolean restricted) { + mRestricted = restricted; + return this; + } + + /** + * Sets the preference write-level sensitivity. + */ + @NonNull + public Builder setWriteSensitivity(@WriteSensitivity int sensitivity) { + mSensitivity = sensitivity; + return this; + } + + /** + * Sets the intent to launch the host app page for this preference. + */ + @NonNull + public Builder setLaunchIntent(@Nullable PendingIntent launchIntent) { + mLaunchIntent = launchIntent; + return this; + } + + /** + * Sets additional fields specific to this preference. Treat all data as optional. + */ + @NonNull + public Builder setExtras(@NonNull Bundle extras) { + mExtras = extras; + return this; + } + + /** + * Constructs an immutable {@link SettingsPreferenceMetadata} object. + */ + @NonNull + public SettingsPreferenceMetadata build() { + return new SettingsPreferenceMetadata(this); + } + } +} diff --git a/core/java/android/service/settings/preferences/SettingsPreferenceService.java b/core/java/android/service/settings/preferences/SettingsPreferenceService.java new file mode 100644 index 000000000000..4a4b5d201f09 --- /dev/null +++ b/core/java/android/service/settings/preferences/SettingsPreferenceService.java @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.settings.preferences; + +import android.Manifest; +import android.annotation.EnforcePermission; +import android.annotation.FlaggedApi; +import android.app.Service; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.os.IBinder; +import android.os.OutcomeReceiver; +import android.os.PermissionEnforcer; +import android.os.RemoteException; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.settingslib.flags.Flags; + +/** + * Base class for a service that exposes its settings preferences to external access. + * <p>This class is to be implemented by apps that contribute to the Android Settings surface. + * Access to this service is permission guarded by + * {@link android.permission.READ_SYSTEM_PREFERENCES} for binding and reading, and guarded by both + * {@link android.permission.READ_SYSTEM_PREFERENCES} and + * {@link android.permission.WRITE_SYSTEM_PREFERENCES} for writing. An additional checks for access + * control are the responsibility of the implementing class. + * + * <p>This implementation must correspond to an exported service declaration in the host app + * AndroidManifest.xml as follows + * <pre class="prettyprint"> + * {@literal + * <service + * android:permission="android.permission.READ_SYSTEM_PREFERENCES" + * android:exported="true"> + * <intent-filter> + * <action android:name="android.service.settings.preferences.action.PREFERENCE_SERVICE" /> + * </intent-filter> + * </service>} + * </pre> + * + * <ul> + * <li>It is recommended to expose the metadata for most, if not all, preferences within a + * settings app, thus implementing {@link #onGetAllPreferenceMetadata}. + * <li>Exposing preferences for read access of their values is up to the implementer, but any + * exposed must be a subset of the preferences exposed in {@link #onGetAllPreferenceMetadata}. + * To expose a preference for read access, the implementation will contain + * {@link #onGetPreferenceValue}. + * <li>Exposing a preference for write access of their values is up to the implementer, but should + * be done so with extra care and consideration, both for security and privacy. These must also + * be a subset of those exposed in {@link #onGetAllPreferenceMetadata}. To expose a preference for + * write access, the implementation will contain {@link #onSetPreferenceValue}. + * </ul> + */ +@FlaggedApi(Flags.FLAG_SETTINGS_CATALYST) +public abstract class SettingsPreferenceService extends Service { + + /** + * Intent Action corresponding to a {@link SettingsPreferenceService}. Note that any checks for + * such services must be accompanied by a check to ensure the host is a system application. + * Given an {@link android.content.pm.ApplicationInfo} you can check for + * {@link android.content.pm.ApplicationInfo#FLAG_SYSTEM}, or when querying + * {@link PackageManager#queryIntentServices} you can provide the flag + * {@link PackageManager#MATCH_SYSTEM_ONLY}. + */ + public static final String ACTION_PREFERENCE_SERVICE = + "android.service.settings.preferences.action.PREFERENCE_SERVICE"; + + /** @hide */ + @NonNull + @Override + public final IBinder onBind(@Nullable Intent intent) { + return new ISettingsPreferenceService.Stub( + PermissionEnforcer.fromContext(getApplicationContext())) { + @EnforcePermission(Manifest.permission.READ_SYSTEM_PREFERENCES) + @Override + public void getAllPreferenceMetadata(MetadataRequest request, + IMetadataCallback callback) { + getAllPreferenceMetadata_enforcePermission(); + onGetAllPreferenceMetadata(request, new OutcomeReceiver<>() { + @Override + public void onResult(MetadataResult result) { + try { + callback.onSuccess(result); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + @Override + public void onError(@NonNull Exception error) { + try { + callback.onFailure(); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + }); + } + + @EnforcePermission(Manifest.permission.READ_SYSTEM_PREFERENCES) + @Override + public void getPreferenceValue(GetValueRequest request, IGetValueCallback callback) { + getPreferenceValue_enforcePermission(); + onGetPreferenceValue(request, new OutcomeReceiver<>() { + @Override + public void onResult(GetValueResult result) { + try { + callback.onSuccess(result); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + @Override + public void onError(@NonNull Exception error) { + try { + callback.onFailure(); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + }); + } + + @EnforcePermission(allOf = { + Manifest.permission.READ_SYSTEM_PREFERENCES, + Manifest.permission.WRITE_SYSTEM_PREFERENCES + }) + @Override + public void setPreferenceValue(SetValueRequest request, ISetValueCallback callback) { + setPreferenceValue_enforcePermission(); + onSetPreferenceValue(request, new OutcomeReceiver<>() { + @Override + public void onResult(SetValueResult result) { + try { + callback.onSuccess(result); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + @Override + public void onError(@NonNull Exception error) { + try { + callback.onFailure(); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + }); + } + }; + } + + /** + * Retrieve the metadata for all exposed settings preferences within this application. This + * data should be a snapshot of their state at the time of this method being called. + * @param request object to specify request parameters + * @param callback object to receive result or failure of request + */ + public abstract void onGetAllPreferenceMetadata( + @NonNull MetadataRequest request, + @NonNull OutcomeReceiver<MetadataResult, Exception> callback); + + /** + * Retrieve the current value of the requested settings preference. If this value is not exposed + * or cannot be obtained for some reason, the corresponding result code will be set on the + * result object. + * @param request object to specify request parameters + * @param callback object to receive result or failure of request + */ + public abstract void onGetPreferenceValue( + @NonNull GetValueRequest request, + @NonNull OutcomeReceiver<GetValueResult, Exception> callback); + + /** + * Set the value within the request to the target settings preference. If this value cannot + * be written for some reason, the corresponding result code will be set on the result object. + * @param request object to specify request parameters + * @param callback object to receive result or failure of request + */ + public abstract void onSetPreferenceValue( + @NonNull SetValueRequest request, + @NonNull OutcomeReceiver<SetValueResult, Exception> callback); +} diff --git a/core/java/android/service/settings/preferences/SettingsPreferenceServiceClient.java b/core/java/android/service/settings/preferences/SettingsPreferenceServiceClient.java new file mode 100644 index 000000000000..39995a47fcbe --- /dev/null +++ b/core/java/android/service/settings/preferences/SettingsPreferenceServiceClient.java @@ -0,0 +1,248 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.service.settings.preferences; + +import static android.service.settings.preferences.SettingsPreferenceService.ACTION_PREFERENCE_SERVICE; + +import android.annotation.CallbackExecutor; +import android.annotation.FlaggedApi; +import android.annotation.TestApi; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.os.IBinder; +import android.os.OutcomeReceiver; +import android.os.RemoteException; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.settingslib.flags.Flags; + +import java.util.List; +import java.util.concurrent.Executor; + +/** + * Client class responsible for binding to and interacting with an instance of + * {@link SettingsPreferenceService}. + * <p>This is a convenience class to handle the lifecycle of the service connection. + * <p>This client will only interact with one instance at a time, + * so if the caller requires multiple instances (multiple applications that provide settings), then + * the caller must create multiple client classes, one for each instance required. To find all + * available services, a caller may query {@link android.content.pm.PackageManager} for applications + * that provide the intent action {@link SettingsPreferenceService#ACTION_PREFERENCE_SERVICE} that + * are also system applications ({@link android.content.pm.ApplicationInfo#FLAG_SYSTEM}). + */ +@FlaggedApi(Flags.FLAG_SETTINGS_CATALYST) +public class SettingsPreferenceServiceClient implements AutoCloseable { + + private final Context mContext; + private final Intent mServiceIntent; + private final ServiceConnection mServiceConnection; + private final boolean mSystemOnly; + private ISettingsPreferenceService mRemoteService; + + /** + * Construct a client for binding to a {@link SettingsPreferenceService} provided by the + * application corresponding to the provided package name. + * @param packageName - package name for which this client will initiate a service binding + */ + public SettingsPreferenceServiceClient(@NonNull Context context, + @NonNull String packageName) { + this(context, packageName, true, null); + } + + /** + * @hide Only to be called directly by test + */ + @TestApi + public SettingsPreferenceServiceClient(@NonNull Context context, + @NonNull String packageName, + boolean systemOnly, + @Nullable ServiceConnection connectionListener) { + mContext = context.getApplicationContext(); + mServiceIntent = new Intent(ACTION_PREFERENCE_SERVICE).setPackage(packageName); + mSystemOnly = systemOnly; + mServiceConnection = createServiceConnection(connectionListener); + } + + /** + * Initiate binding to service. + * <p>If no service exists for the package provided or the package is not for a system + * application, no binding will occur. + */ + public void start() { + PackageManager pm = mContext.getPackageManager(); + PackageManager.ResolveInfoFlags flags; + if (mSystemOnly) { + flags = PackageManager.ResolveInfoFlags.of(PackageManager.MATCH_SYSTEM_ONLY); + } else { + flags = PackageManager.ResolveInfoFlags.of(PackageManager.MATCH_ALL); + } + List<ResolveInfo> infos = pm.queryIntentServices(mServiceIntent, flags); + if (infos.size() == 1) { + mContext.bindService(mServiceIntent, mServiceConnection, Context.BIND_AUTO_CREATE); + } + } + + /** + * If there is an active service binding, unbind from that service. + */ + public void stop() { + if (mRemoteService != null) { + mRemoteService = null; + mContext.unbindService(mServiceConnection); + } + } + + /** + * Retrieve the metadata for all exposed settings preferences within the application. + * @param request object to specify request parameters + * @param executor {@link Executor} on which to invoke the receiver + * @param receiver callback to receive the result or failure + */ + public void getAllPreferenceMetadata( + @NonNull MetadataRequest request, + @CallbackExecutor @NonNull Executor executor, + @NonNull OutcomeReceiver<MetadataResult, Exception> receiver) { + if (mRemoteService == null) { + executor.execute(() -> + receiver.onError(new IllegalStateException("Service not ready"))); + return; + } + try { + mRemoteService.getAllPreferenceMetadata(request, new IMetadataCallback.Stub() { + @Override + public void onSuccess(MetadataResult result) { + executor.execute(() -> receiver.onResult(result)); + } + + @Override + public void onFailure() { + executor.execute(() -> receiver.onError( + new IllegalStateException("Service call failure"))); + } + }); + } catch (RemoteException | RuntimeException e) { + executor.execute(() -> receiver.onError(e)); + } + } + + /** + * Retrieve the current value of the requested settings preference. + * @param request object to specify request parameters + * @param executor {@link Executor} on which to invoke the receiver + * @param receiver callback to receive the result or failure + */ + public void getPreferenceValue(@NonNull GetValueRequest request, + @CallbackExecutor @NonNull Executor executor, + @NonNull OutcomeReceiver<GetValueResult, Exception> receiver) { + if (mRemoteService == null) { + executor.execute(() -> + receiver.onError(new IllegalStateException("Service not ready"))); + return; + } + try { + mRemoteService.getPreferenceValue(request, new IGetValueCallback.Stub() { + @Override + public void onSuccess(GetValueResult result) { + executor.execute(() -> receiver.onResult(result)); + } + + @Override + public void onFailure() { + executor.execute(() -> receiver.onError( + new IllegalStateException("Service call failure"))); + } + }); + } catch (RemoteException | RuntimeException e) { + executor.execute(() -> receiver.onError(e)); + } + } + + /** + * Set the value on the target settings preference. + * @param request object to specify request parameters + * @param executor {@link Executor} on which to invoke the receiver + * @param receiver callback to receive the result or failure + */ + public void setPreferenceValue(@NonNull SetValueRequest request, + @CallbackExecutor @NonNull Executor executor, + @NonNull OutcomeReceiver<SetValueResult, Exception> receiver) { + if (mRemoteService == null) { + executor.execute(() -> + receiver.onError(new IllegalStateException("Service not ready"))); + return; + } + try { + mRemoteService.setPreferenceValue(request, new ISetValueCallback.Stub() { + @Override + public void onSuccess(SetValueResult result) { + executor.execute(() -> receiver.onResult(result)); + } + + @Override + public void onFailure() { + executor.execute(() -> receiver.onError( + new IllegalStateException("Service call failure"))); + } + }); + } catch (RemoteException | RuntimeException e) { + executor.execute(() -> receiver.onError(e)); + } + } + + @NonNull + private ServiceConnection createServiceConnection(@Nullable ServiceConnection listener) { + return new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + mRemoteService = getPreferenceServiceInterface(service); + if (listener != null) { + listener.onServiceConnected(name, service); + } + } + + @Override + public void onServiceDisconnected(ComponentName name) { + mRemoteService = null; + if (listener != null) { + listener.onServiceDisconnected(name); + } + } + }; + } + + @NonNull + private ISettingsPreferenceService getPreferenceServiceInterface(@NonNull IBinder service) { + return ISettingsPreferenceService.Stub.asInterface(service); + } + + /** + * This client handles a resource, thus is it important to appropriately close that resource + * when it is no longer needed. + * <p>This method is provided by {@link AutoCloseable} and calling it + * will unbind any service binding. + */ + @Override + public void close() { + stop(); + } +} diff --git a/core/java/android/service/settings/preferences/SettingsPreferenceValue.java b/core/java/android/service/settings/preferences/SettingsPreferenceValue.java new file mode 100644 index 000000000000..f056e34a0dd2 --- /dev/null +++ b/core/java/android/service/settings/preferences/SettingsPreferenceValue.java @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.service.settings.preferences; + +import android.annotation.FlaggedApi; +import android.annotation.IntDef; +import android.annotation.SuppressLint; +import android.os.Parcel; +import android.os.Parcelable; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.settingslib.flags.Flags; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * This objects represents a value that can be used for a particular settings preference. + * <p>The data type for the value will correspond to {@link #getType}. For possible types, see + * constants below, such as {@link #TYPE_BOOLEAN} and {@link #TYPE_STRING}. + * Depending on the type, the corresponding getter will contain its value. All other getters will + * return default values (boolean returns false, String returns null) so they should not be used. + * <p>See documentation on the constants for which getter method should be used. + */ +@FlaggedApi(Flags.FLAG_SETTINGS_CATALYST) +public final class SettingsPreferenceValue implements Parcelable { + + @Type + private final int mType; + private final boolean mBooleanValue; + private final long mLongValue; + private final double mDoubleValue; + @Nullable + private final String mStringValue; + + /** + * Returns the type indicator for Preference value. + */ + @Type + public int getType() { + return mType; + } + + /** + * Returns the boolean value for Preference if type is {@link #TYPE_BOOLEAN}. + */ + public boolean getBooleanValue() { + return mBooleanValue; + } + + /** + * Returns the long value for Preference if type is {@link #TYPE_LONG}. + */ + public long getLongValue() { + return mLongValue; + } + + /** + * Returns the double value for Preference if type is {@link #TYPE_DOUBLE}. + */ + public double getDoubleValue() { + return mDoubleValue; + } + + /** + * Returns the string value for Preference if type is {@link #TYPE_STRING}. + */ + @Nullable + public String getStringValue() { + return mStringValue; + } + + /** @hide */ + @IntDef(prefix = { "TYPE_" }, value = { + TYPE_BOOLEAN, + TYPE_LONG, + TYPE_DOUBLE, + TYPE_STRING, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface Type {} + + /** Value is of type boolean. Access via {@link #getBooleanValue}. */ + public static final int TYPE_BOOLEAN = 0; + /** Value is of type long. Access via {@link #getLongValue()}. */ + public static final int TYPE_LONG = 1; + /** Value is of type double. Access via {@link #getDoubleValue()}. */ + public static final int TYPE_DOUBLE = 2; + /** Value is of type string. Access via {@link #getStringValue}. */ + public static final int TYPE_STRING = 3; + + private SettingsPreferenceValue(@NonNull Builder builder) { + mType = builder.mType; + mBooleanValue = builder.mBooleanValue; + mLongValue = builder.mLongValue; + mDoubleValue = builder.mDoubleValue; + mStringValue = builder.mStringValue; + } + + private SettingsPreferenceValue(@NonNull Parcel in) { + mType = in.readInt(); + mBooleanValue = in.readBoolean(); + mLongValue = in.readLong(); + mDoubleValue = in.readDouble(); + mStringValue = in.readString8(); + } + + /** @hide */ + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mType); + dest.writeBoolean(mBooleanValue); + dest.writeLong(mLongValue); + dest.writeDouble(mDoubleValue); + dest.writeString8(mStringValue); + } + + /** @hide */ + @Override + public int describeContents() { + return 0; + } + + /** + * Parcelable Creator for {@link SettingsPreferenceValue}. + */ + @NonNull + public static final Creator<SettingsPreferenceValue> CREATOR = new Creator<>() { + @Override + public SettingsPreferenceValue createFromParcel(@NonNull Parcel in) { + return new SettingsPreferenceValue(in); + } + + @Override + public SettingsPreferenceValue[] newArray(int size) { + return new SettingsPreferenceValue[size]; + } + }; + + /** + * Builder to construct {@link SettingsPreferenceValue}. + */ + public static final class Builder { + @Type + private final int mType; + private boolean mBooleanValue; + private long mLongValue; + private double mDoubleValue; + private String mStringValue; + + /** + * Create Builder instance. + * @param type type indicator for preference value + */ + public Builder(@Type int type) { + mType = type; + } + + /** + * Sets boolean value for Preference. + */ + @SuppressLint("MissingGetterMatchingBuilder") + @NonNull + public Builder setBooleanValue(boolean booleanValue) { + mBooleanValue = booleanValue; + return this; + } + + /** + * Sets long value for Preference. + */ + @NonNull + public Builder setLongValue(long longValue) { + mLongValue = longValue; + return this; + } + + /** + * Sets floating point value for Preference. + */ + @NonNull + public Builder setDoubleValue(double doubleValue) { + mDoubleValue = doubleValue; + return this; + } + + /** + * Sets string value for Preference. + */ + @NonNull + public Builder setStringValue(@Nullable String stringValue) { + mStringValue = stringValue; + return this; + } + + /** + * Constructs an immutable {@link SettingsPreferenceValue} object. + */ + @NonNull + public SettingsPreferenceValue build() { + return new SettingsPreferenceValue(this); + } + } +} diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java index bce51f297aff..1df3b4332754 100644 --- a/core/java/android/telephony/PhoneStateListener.java +++ b/core/java/android/telephony/PhoneStateListener.java @@ -37,6 +37,7 @@ import android.telephony.TelephonyManager.EmergencyCallbackModeType; import android.telephony.emergency.EmergencyNumber; import android.telephony.ims.ImsReasonInfo; import android.telephony.ims.MediaQualityStatus; +import android.telephony.satellite.NtnSignalStrength; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telephony.IPhoneStateListener; @@ -1706,6 +1707,11 @@ public class PhoneStateListener { @NetworkRegistrationInfo.ServiceType int[] availableServices) { // not supported on the deprecated interface - Use TelephonyCallback instead } + + public final void onCarrierRoamingNtnSignalStrengthChanged( + @NonNull NtnSignalStrength ntnSignalStrength) { + // not supported on the deprecated interface - Use TelephonyCallback instead + } } private void log(String s) { diff --git a/core/java/android/telephony/TelephonyCallback.java b/core/java/android/telephony/TelephonyCallback.java index 64a5533cbe69..0d1dc4611343 100644 --- a/core/java/android/telephony/TelephonyCallback.java +++ b/core/java/android/telephony/TelephonyCallback.java @@ -30,6 +30,7 @@ import android.telephony.emergency.EmergencyNumber; import android.telephony.ims.ImsReasonInfo; import android.telephony.ims.MediaQualityStatus; import android.telephony.ims.MediaThreshold; +import android.telephony.satellite.NtnSignalStrength; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; @@ -695,6 +696,15 @@ public class TelephonyCallback { public static final int EVENT_CARRIER_ROAMING_NTN_AVAILABLE_SERVICES_CHANGED = 44; /** + * Event for listening to carrier roaming non-terrestrial network signal strength changes. + * + * @see CarrierRoamingNtnModeListener + * + * @hide + */ + public static final int EVENT_CARRIER_ROAMING_NTN_SIGNAL_STRENGTH_CHANGED = 45; + + /** * @hide */ @IntDef(prefix = {"EVENT_"}, value = { @@ -741,7 +751,8 @@ public class TelephonyCallback { EVENT_SIMULTANEOUS_CELLULAR_CALLING_SUBSCRIPTIONS_CHANGED, EVENT_CARRIER_ROAMING_NTN_MODE_CHANGED, EVENT_CARRIER_ROAMING_NTN_ELIGIBLE_STATE_CHANGED, - EVENT_CARRIER_ROAMING_NTN_AVAILABLE_SERVICES_CHANGED + EVENT_CARRIER_ROAMING_NTN_AVAILABLE_SERVICES_CHANGED, + EVENT_CARRIER_ROAMING_NTN_SIGNAL_STRENGTH_CHANGED }) @Retention(RetentionPolicy.SOURCE) public @interface TelephonyEvent { @@ -1805,6 +1816,14 @@ public class TelephonyCallback { */ default void onCarrierRoamingNtnAvailableServicesChanged( @NetworkRegistrationInfo.ServiceType List<Integer> availableServices) {} + + /** + * Callback invoked when carrier roaming non-terrestrial network signal strength changes. + * + * @param ntnSignalStrength non-terrestrial network signal strength. + */ + default void onCarrierRoamingNtnSignalStrengthChanged( + @NonNull NtnSignalStrength ntnSignalStrength) {} } /** @@ -2270,5 +2289,18 @@ public class TelephonyCallback { Binder.withCleanCallingIdentity(() -> mExecutor.execute( () -> listener.onCarrierRoamingNtnAvailableServicesChanged(ServiceList))); } + + public void onCarrierRoamingNtnSignalStrengthChanged( + @NonNull NtnSignalStrength ntnSignalStrength) { + if (!Flags.carrierRoamingNbIotNtn()) return; + + CarrierRoamingNtnModeListener listener = + (CarrierRoamingNtnModeListener) mTelephonyCallbackWeakRef.get(); + if (listener == null) return; + + Binder.withCleanCallingIdentity(() -> mExecutor.execute( + () -> listener.onCarrierRoamingNtnSignalStrengthChanged(ntnSignalStrength))); + + } } } diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java index 1dab2cf75594..90b0bb34c145 100644 --- a/core/java/android/telephony/TelephonyRegistryManager.java +++ b/core/java/android/telephony/TelephonyRegistryManager.java @@ -47,6 +47,7 @@ import android.telephony.emergency.EmergencyNumber; import android.telephony.ims.ImsCallSession; import android.telephony.ims.ImsReasonInfo; import android.telephony.ims.MediaQualityStatus; +import android.telephony.satellite.NtnSignalStrength; import android.telephony.satellite.SatelliteStateChangeListener; import android.util.ArrayMap; import android.util.ArraySet; @@ -1137,6 +1138,23 @@ public class TelephonyRegistryManager { } /** + * Notify external listeners that carrier roaming non-terrestrial network + * signal strength changed. + * @param subId subscription ID. + * @param ntnSignalStrength non-terrestrial network signal strength. + * @hide + */ + public final void notifyCarrierRoamingNtnSignalStrengthChanged(int subId, + @NonNull NtnSignalStrength ntnSignalStrength) { + try { + sRegistry.notifyCarrierRoamingNtnSignalStrengthChanged(subId, ntnSignalStrength); + } catch (RemoteException ex) { + // system server crash + throw ex.rethrowFromSystemServer(); + } + } + + /** * Processes potential event changes from the provided {@link TelephonyCallback}. * * @param telephonyCallback callback for monitoring callback changes to the telephony state. @@ -1293,6 +1311,7 @@ public class TelephonyRegistryManager { eventList.add(TelephonyCallback.EVENT_CARRIER_ROAMING_NTN_MODE_CHANGED); eventList.add(TelephonyCallback.EVENT_CARRIER_ROAMING_NTN_ELIGIBLE_STATE_CHANGED); eventList.add(TelephonyCallback.EVENT_CARRIER_ROAMING_NTN_AVAILABLE_SERVICES_CHANGED); + eventList.add(TelephonyCallback.EVENT_CARRIER_ROAMING_NTN_SIGNAL_STRENGTH_CHANGED); } return eventList; } diff --git a/core/java/android/text/flags/flags.aconfig b/core/java/android/text/flags/flags.aconfig index 02923eda308e..f43f172d7d5b 100644 --- a/core/java/android/text/flags/flags.aconfig +++ b/core/java/android/text/flags/flags.aconfig @@ -163,10 +163,12 @@ flag { } flag { - name: "typeface_redesign" + name: "typeface_redesign_readonly" namespace: "text" description: "Decouple variation settings, weight and style information from Typeface class" bug: "361260253" + # This feature does not support runtime flag switch which leads crash in System UI. + is_fixed_read_only: true } flag { diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java index 0241e9437950..a1a9fc697271 100644 --- a/core/java/android/view/Display.java +++ b/core/java/android/view/Display.java @@ -1786,7 +1786,12 @@ public final class Display { * {@code getWindowManager()} or {@code getSystemService(Context.WINDOW_SERVICE)}), the * returned metrics provide the size of the current app window. As a result, in * multi-window mode, the returned size can be smaller than the size of the device - * screen. + * screen. System decoration handling may vary depending on API level: + * <ul> + * <li>API level 35 and above, the window size will be returned. + * <li>API level 34 and below, the window size minus system decoration areas and + * display cutout is returned. + * </ul> * <li>If metrics are requested from a non-activity context (for example, the application * context, where the WindowManager is accessed by * {@code getApplicationContext().getSystemService(Context.WINDOW_SERVICE)}), the diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java index 8f112f338a00..4ff04d5c1fa6 100644 --- a/core/java/android/view/DisplayInfo.java +++ b/core/java/android/view/DisplayInfo.java @@ -447,7 +447,6 @@ public final class DisplayInfo implements Parcelable { && Objects.equals(displayCutout, other.displayCutout) && rotation == other.rotation && modeId == other.modeId - && renderFrameRate == other.renderFrameRate && hasArrSupport == other.hasArrSupport && Objects.equals(frameRateCategoryRate, other.frameRateCategoryRate) && defaultModeId == other.defaultModeId @@ -705,6 +704,9 @@ public final class DisplayInfo implements Parcelable { if (refreshRateOverride > 0) { return refreshRateOverride; } + if (renderFrameRate > 0) { + return renderFrameRate; + } if (supportedModes.length == 0) { return 0; } diff --git a/core/java/android/view/HapticScrollFeedbackProvider.java b/core/java/android/view/HapticScrollFeedbackProvider.java index 0001176220b5..c3fb855eb1ff 100644 --- a/core/java/android/view/HapticScrollFeedbackProvider.java +++ b/core/java/android/view/HapticScrollFeedbackProvider.java @@ -16,6 +16,8 @@ package android.view; +import static android.view.flags.Flags.dynamicViewRotaryHapticsConfiguration; + import android.annotation.NonNull; import com.android.internal.annotations.VisibleForTesting; @@ -41,13 +43,8 @@ public class HapticScrollFeedbackProvider implements ScrollFeedbackProvider { private final View mView; private final ViewConfiguration mViewConfig; - /** - * Flag to disable the logic in this class if the View-based scroll haptics implementation is - * enabled. If {@code false}, this class will continue to run despite the View's scroll - * haptics implementation being enabled. This value should be set to {@code true} when this - * class is directly used by the View class. - */ - private final boolean mDisabledIfViewPlaysScrollHaptics; + /** Whether or not this provider is being used directly by the View class. */ + private final boolean mIsFromView; // Info about the cause of the latest scroll event. @@ -65,17 +62,23 @@ public class HapticScrollFeedbackProvider implements ScrollFeedbackProvider { private boolean mHapticScrollFeedbackEnabled = false; public HapticScrollFeedbackProvider(@NonNull View view) { - this(view, ViewConfiguration.get(view.getContext()), - /* disabledIfViewPlaysScrollHaptics= */ true); + this(view, ViewConfiguration.get(view.getContext()), /* isFromView= */ false); } /** @hide */ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) public HapticScrollFeedbackProvider( - View view, ViewConfiguration viewConfig, boolean disabledIfViewPlaysScrollHaptics) { + View view, ViewConfiguration viewConfig, boolean isFromView) { mView = view; mViewConfig = viewConfig; - mDisabledIfViewPlaysScrollHaptics = disabledIfViewPlaysScrollHaptics; + mIsFromView = isFromView; + if (dynamicViewRotaryHapticsConfiguration() && !isFromView) { + // Disable the View class's rotary scroll feedback logic if this provider is not being + // directly used by the View class. This is to avoid double rotary scroll feedback: + // one from the View class, and one from this provider instance (i.e. mute the View + // class's rotary feedback and enable this provider). + view.disableRotaryScrollFeedback(); + } } @Override @@ -151,7 +154,8 @@ public class HapticScrollFeedbackProvider implements ScrollFeedbackProvider { mAxis = axis; mDeviceId = deviceId; - if (mDisabledIfViewPlaysScrollHaptics + if (!dynamicViewRotaryHapticsConfiguration() + && !mIsFromView && (source == InputDevice.SOURCE_ROTARY_ENCODER) && mViewConfig.isViewBasedRotaryEncoderHapticScrollFeedbackEnabled()) { mHapticScrollFeedbackEnabled = false; diff --git a/core/java/android/view/ImeFocusController.java b/core/java/android/view/ImeFocusController.java index 97148969e17f..2d2f79d76008 100644 --- a/core/java/android/view/ImeFocusController.java +++ b/core/java/android/view/ImeFocusController.java @@ -23,6 +23,7 @@ import android.annotation.NonNull; import android.annotation.UiThread; import android.util.Log; import android.util.proto.ProtoOutputStream; +import android.view.inputmethod.Flags; import android.view.inputmethod.InputMethodManager; import com.android.internal.inputmethod.InputMethodDebug; @@ -150,6 +151,17 @@ public final class ImeFocusController { if (!mHasImeFocus || isInLocalFocusMode(windowAttribute)) { return InputMethodManager.DISPATCH_NOT_HANDLED; } + if (Flags.refactorInsetsController() && event instanceof KeyEvent keyEvent + && keyEvent.getKeyCode() == KeyEvent.KEYCODE_BACK) { + final var insetsController = mViewRootImpl.getInsetsController(); + if (insetsController.getAnimationType(WindowInsets.Type.ime()) + == InsetsController.ANIMATION_TYPE_HIDE + || insetsController.isPredictiveBackImeHideAnimInProgress()) { + // if there is an ongoing hide animation, the back event should not be dispatched + // to the IME. + return InputMethodManager.DISPATCH_NOT_HANDLED; + } + } final InputMethodManager imm = mViewRootImpl.mContext.getSystemService(InputMethodManager.class); if (imm == null) { diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java index 26ca813a9caa..b0813f3a98f6 100644 --- a/core/java/android/view/InsetsController.java +++ b/core/java/android/view/InsetsController.java @@ -1910,7 +1910,8 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation mImeSourceConsumer.onWindowFocusLost(); } - @VisibleForTesting + /** Returns the current {@link AnimationType} of an {@link InsetsType}. */ + @VisibleForTesting(visibility = PACKAGE) public @AnimationType int getAnimationType(@InsetsType int type) { for (int i = mRunningAnimations.size() - 1; i >= 0; i--) { InsetsAnimationControlRunner control = mRunningAnimations.get(i).runner; diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java index dddc408ed9db..38e4e2760d25 100644 --- a/core/java/android/view/KeyEvent.java +++ b/core/java/android/view/KeyEvent.java @@ -935,7 +935,6 @@ public class KeyEvent extends InputEvent implements Parcelable { */ public static final int KEYCODE_MACRO_4 = 316; /** Key code constant: To open emoji picker */ - @FlaggedApi(Flags.FLAG_EMOJI_AND_SCREENSHOT_KEYCODES_AVAILABLE) public static final int KEYCODE_EMOJI_PICKER = 317; /** * Key code constant: To take a screenshot @@ -944,15 +943,80 @@ public class KeyEvent extends InputEvent implements Parcelable { * unlike {@code KEYCODE_SYSRQ} which is sent to the app first and only if the app * doesn't handle it, the framework handles it (to take a screenshot). */ - @FlaggedApi(Flags.FLAG_EMOJI_AND_SCREENSHOT_KEYCODES_AVAILABLE) public static final int KEYCODE_SCREENSHOT = 318; + /** Key code constant: To start dictate to an input field */ + @FlaggedApi(Flags.FLAG_ENABLE_NEW_25Q2_KEYCODES) + public static final int KEYCODE_DICTATE = 319; + /** + * Key code constant: AC New + * + * e.g. To create a new instance of a window, open a new tab, etc. + */ + @FlaggedApi(Flags.FLAG_ENABLE_NEW_25Q2_KEYCODES) + public static final int KEYCODE_NEW = 320; + /** + * Key code constant: AC Close + * + * e.g. To close current instance of the application window, close the current tab, etc. + */ + @FlaggedApi(Flags.FLAG_ENABLE_NEW_25Q2_KEYCODES) + public static final int KEYCODE_CLOSE = 321; + /** Key code constant: To toggle 'Do Not Disturb' mode */ + @FlaggedApi(Flags.FLAG_ENABLE_NEW_25Q2_KEYCODES) + public static final int KEYCODE_DO_NOT_DISTURB = 322; + /** Key code constant: To Print */ + @FlaggedApi(Flags.FLAG_ENABLE_NEW_25Q2_KEYCODES) + public static final int KEYCODE_PRINT = 323; + /** Key code constant: To Lock the screen */ + @FlaggedApi(Flags.FLAG_ENABLE_NEW_25Q2_KEYCODES) + public static final int KEYCODE_LOCK = 324; + /** Key code constant: To toggle fullscreen mode (on the current application) */ + @FlaggedApi(Flags.FLAG_ENABLE_NEW_25Q2_KEYCODES) + public static final int KEYCODE_FULLSCREEN = 325; + /** Key code constant: F13 key. */ + @FlaggedApi(Flags.FLAG_ENABLE_NEW_25Q2_KEYCODES) + public static final int KEYCODE_F13 = 326; + /** Key code constant: F14 key. */ + @FlaggedApi(Flags.FLAG_ENABLE_NEW_25Q2_KEYCODES) + public static final int KEYCODE_F14 = 327; + /** Key code constant: F15 key. */ + @FlaggedApi(Flags.FLAG_ENABLE_NEW_25Q2_KEYCODES) + public static final int KEYCODE_F15 = 328; + /** Key code constant: F16 key. */ + @FlaggedApi(Flags.FLAG_ENABLE_NEW_25Q2_KEYCODES) + public static final int KEYCODE_F16 = 329; + /** Key code constant: F17 key. */ + @FlaggedApi(Flags.FLAG_ENABLE_NEW_25Q2_KEYCODES) + public static final int KEYCODE_F17 = 330; + /** Key code constant: F18 key. */ + @FlaggedApi(Flags.FLAG_ENABLE_NEW_25Q2_KEYCODES) + public static final int KEYCODE_F18 = 331; + /** Key code constant: F19 key. */ + @FlaggedApi(Flags.FLAG_ENABLE_NEW_25Q2_KEYCODES) + public static final int KEYCODE_F19 = 332; + /** Key code constant: F20 key. */ + @FlaggedApi(Flags.FLAG_ENABLE_NEW_25Q2_KEYCODES) + public static final int KEYCODE_F20 = 333; + /** Key code constant: F21 key. */ + @FlaggedApi(Flags.FLAG_ENABLE_NEW_25Q2_KEYCODES) + public static final int KEYCODE_F21 = 334; + /** Key code constant: F22 key. */ + @FlaggedApi(Flags.FLAG_ENABLE_NEW_25Q2_KEYCODES) + public static final int KEYCODE_F22 = 335; + /** Key code constant: F23 key. */ + @FlaggedApi(Flags.FLAG_ENABLE_NEW_25Q2_KEYCODES) + public static final int KEYCODE_F23 = 336; + /** Key code constant: F24 key. */ + @FlaggedApi(Flags.FLAG_ENABLE_NEW_25Q2_KEYCODES) + public static final int KEYCODE_F24 = 337; /** * Integer value of the last KEYCODE. Increases as new keycodes are added to KeyEvent. * @hide */ @TestApi - public static final int LAST_KEYCODE = KEYCODE_SCREENSHOT; + @SuppressWarnings("FlaggedApi") + public static final int LAST_KEYCODE = KEYCODE_F24; /** @hide */ @IntDef(prefix = {"KEYCODE_"}, value = { @@ -1275,6 +1339,25 @@ public class KeyEvent extends InputEvent implements Parcelable { KEYCODE_MACRO_4, KEYCODE_EMOJI_PICKER, KEYCODE_SCREENSHOT, + KEYCODE_DICTATE, + KEYCODE_NEW, + KEYCODE_CLOSE, + KEYCODE_DO_NOT_DISTURB, + KEYCODE_PRINT, + KEYCODE_LOCK, + KEYCODE_FULLSCREEN, + KEYCODE_F13, + KEYCODE_F14, + KEYCODE_F15, + KEYCODE_F16, + KEYCODE_F17, + KEYCODE_F18, + KEYCODE_F19, + KEYCODE_F20, + KEYCODE_F21, + KEYCODE_F22, + KEYCODE_F23, + KEYCODE_F24, }) @Retention(RetentionPolicy.SOURCE) @interface KeyCode {} diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index df54d310059f..206c73756088 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -2989,7 +2989,6 @@ public final class SurfaceControl implements Parcelable { private void apply(boolean sync, boolean oneWay) { applyResizedSurfaces(); notifyReparentedSurfaces(); - nativeApplyTransaction(mNativeObject, sync, oneWay); if (SurfaceControlRegistry.sCallStackDebuggingEnabled) { SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging( @@ -2998,6 +2997,7 @@ public final class SurfaceControl implements Parcelable { if (mCalls != null) { mCalls.clear(); } + nativeApplyTransaction(mNativeObject, sync, oneWay); } /** diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 618843ccb005..049189f8af8d 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -65,6 +65,7 @@ import static com.android.internal.util.FrameworkStatsLog.TOUCH_GESTURE_CLASSIFI import static com.android.internal.util.FrameworkStatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__SINGLE_TAP; import static com.android.internal.util.FrameworkStatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__UNKNOWN_CLASSIFICATION; import static com.android.window.flags.Flags.FLAG_DELEGATE_UNHANDLED_DRAGS; +import static com.android.window.flags.Flags.FLAG_SUPPORTS_DRAG_ASSISTANT_TO_MULTIWINDOW; import static java.lang.Math.max; @@ -91,6 +92,8 @@ import android.annotation.TestApi; import android.annotation.UiContext; import android.annotation.UiThread; import android.app.PendingIntent; +import android.app.jank.AppJankStats; +import android.app.jank.JankTracker; import android.compat.annotation.UnsupportedAppUsage; import android.content.AutofillOptions; import android.content.ClipData; @@ -5550,10 +5553,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /** * Flag indicating that this drag will result in the caller activity's task to be hidden for the - * duration of the drag, this means that the source activity will not receive drag events for - * the current drag gesture. Only the current voice interaction service may use this flag. - * @hide + * duration of the drag, which means that the source activity will not receive drag events for + * the current drag gesture. Only the current + * {@link android.service.voice.VoiceInteractionService} may use this flag. */ + @FlaggedApi(FLAG_SUPPORTS_DRAG_ASSISTANT_TO_MULTIWINDOW) public static final int DRAG_FLAG_HIDE_CALLING_TASK_ON_DRAG_START = 1 << 14; /** @@ -16750,9 +16754,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mPrivateFlags4 |= PFLAG4_ROTARY_HAPTICS_DETERMINED; } } - final boolean processForRotaryScrollHaptics = - isRotaryEncoderEvent && ((mPrivateFlags4 & PFLAG4_ROTARY_HAPTICS_ENABLED) != 0); - if (processForRotaryScrollHaptics) { + if (isRotaryEncoderEvent && ((mPrivateFlags4 & PFLAG4_ROTARY_HAPTICS_ENABLED) != 0)) { mPrivateFlags4 &= ~PFLAG4_ROTARY_HAPTICS_SCROLL_SINCE_LAST_ROTARY_INPUT; mPrivateFlags4 |= PFLAG4_ROTARY_HAPTICS_WAITING_FOR_SCROLL_EVENT; } @@ -16769,7 +16771,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // Process scroll haptics after `onGenericMotionEvent`, since that's where scrolling usually // happens. Some views may return false from `onGenericMotionEvent` even if they have done // scrolling, so disregard the return value when processing for scroll haptics. - if (processForRotaryScrollHaptics) { + // Check for `PFLAG4_ROTARY_HAPTICS_ENABLED` again, because the View implementation may + // call `disableRotaryScrollFeedback` in `onGenericMotionEvent`, which could change the + // value of `PFLAG4_ROTARY_HAPTICS_ENABLED`. + if (isRotaryEncoderEvent && ((mPrivateFlags4 & PFLAG4_ROTARY_HAPTICS_ENABLED) != 0)) { if ((mPrivateFlags4 & PFLAG4_ROTARY_HAPTICS_SCROLL_SINCE_LAST_ROTARY_INPUT) != 0) { doRotaryProgressForScrollHaptics(event); } else { @@ -18712,7 +18717,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, private HapticScrollFeedbackProvider getScrollFeedbackProvider() { if (mScrollFeedbackProvider == null) { mScrollFeedbackProvider = new HapticScrollFeedbackProvider(this, - ViewConfiguration.get(mContext), /* disabledIfViewPlaysScrollHaptics= */ false); + ViewConfiguration.get(mContext), /* isFromView= */ true); } return mScrollFeedbackProvider; } @@ -18742,6 +18747,21 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** + * Disables the rotary scroll feedback implementation of the View class. + * + * <p>Note that this does NOT disable all rotary scroll feedback; it just disables the logic + * implemented within the View class. The child implementation of the View may implement its own + * rotary scroll feedback logic or use {@link ScrollFeedbackProvider} to generate rotary scroll + * feedback. + */ + void disableRotaryScrollFeedback() { + // Force set PFLAG4_ROTARY_HAPTICS_DETERMINED to avoid recalculating + // PFLAG4_ROTARY_HAPTICS_ENABLED under any circumstance. + mPrivateFlags4 |= PFLAG4_ROTARY_HAPTICS_DETERMINED; + mPrivateFlags4 &= ~PFLAG4_ROTARY_HAPTICS_ENABLED; + } + + /** * This is called in response to an internal scroll in this view (i.e., the * view scrolled its own contents). This is typically as a result of * {@link #scrollBy(int, int)} or {@link #scrollTo(int, int)} having been @@ -23882,12 +23902,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } else { draw(canvas); } - } - // For VRR to vote the preferred frame rate - if (sToolkitSetFrameRateReadOnlyFlagValue - && sToolkitFrameRateViewEnablingReadOnlyFlagValue) { - votePreferredFrameRate(); + // For VRR to vote the preferred frame rate + if (sToolkitSetFrameRateReadOnlyFlagValue + && sToolkitFrameRateViewEnablingReadOnlyFlagValue) { + votePreferredFrameRate(); + } } } finally { renderNode.endRecording(); @@ -34420,4 +34440,21 @@ public class View implements Drawable.Callback, KeyEvent.Callback, boolean getSelfRequestedFrameRateFlag() { return (mPrivateFlags4 & PFLAG4_SELF_REQUESTED_FRAME_RATE) != 0; } + + /** + * Called from apps when they want to report jank stats to the system. + * @param appJankStats the stats that will be merged with the stats collected by the system. + */ + @FlaggedApi(android.app.jank.Flags.FLAG_DETAILED_APP_JANK_METRICS_API) + public void reportAppJankStats(@NonNull AppJankStats appJankStats) { + getRootView().reportAppJankStats(appJankStats); + } + + /** + * Called by widgets to get a reference to JankTracker in order to update states. + * @hide + */ + public @Nullable JankTracker getJankTracker() { + return getRootView().getJankTracker(); + } } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 9a2aa0b8a682..75d2da1b70e4 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -9951,11 +9951,13 @@ public final class ViewRootImpl implements ViewParent, return false; } - if (!mIsDrawing) { - destroyHardwareRenderer(); - } else { - Log.e(mTag, "Attempting to destroy the window while drawing!\n" + - " window=" + this + ", title=" + mWindowAttributes.getTitle()); + if (!com.android.graphics.hwui.flags.Flags.removeVriSketchyDestroy()) { + if (!mIsDrawing) { + destroyHardwareRenderer(); + } else { + Log.e(mTag, "Attempting to destroy the window while drawing!\n" + + " window=" + this + ", title=" + mWindowAttributes.getTitle()); + } } mHandler.sendEmptyMessage(MSG_DIE); return true; @@ -9976,9 +9978,9 @@ public final class ViewRootImpl implements ViewParent, dispatchDetachedFromWindow(); } - if (mAdded && !mFirst) { - destroyHardwareRenderer(); + destroyHardwareRenderer(); + if (mAdded && !mFirst) { if (mView != null) { int viewVisibility = mView.getVisibility(); boolean viewVisibilityChanged = mViewVisibility != viewVisibility; diff --git a/core/java/android/view/WindowMetrics.java b/core/java/android/view/WindowMetrics.java index 8bcc9de118e2..12af692a4556 100644 --- a/core/java/android/view/WindowMetrics.java +++ b/core/java/android/view/WindowMetrics.java @@ -107,8 +107,8 @@ public final class WindowMetrics { * and display cutout areas depending on the calling context and target SDK level. Please refer * to {@link Display#getSize(Point)} for details. * <p> - * The value reported by {@link Display#getSize(Point)} excluding system decoration areas can be - * obtained by using: + * The following code snippet shows how to get the bounds excluding navigation bars and display + * cutout: * <pre class="prettyprint"> * final WindowMetrics metrics = windowManager.getCurrentWindowMetrics(); * // Gets all excluding insets diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java index 14652035438d..0204517e869a 100644 --- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java +++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java @@ -3866,8 +3866,14 @@ public class AccessibilityNodeInfo implements Parcelable { * Sets the view for which the view represented by this info serves as a * label for accessibility purposes. * + * @deprecated Use {@link #addLabeledBy(View)} on the labeled node instead, + * since {@link #getLabeledByList()} and {@link #getLabeledBy()} on the + * labeled node are not automatically populated when this method is used. + * * @param labeled The view for which this info serves as a label. */ + @FlaggedApi(Flags.FLAG_DEPRECATE_ANI_LABEL_FOR_APIS) + @Deprecated public void setLabelFor(View labeled) { setLabelFor(labeled, AccessibilityNodeProvider.HOST_VIEW_ID); } @@ -3888,9 +3894,15 @@ public class AccessibilityNodeInfo implements Parcelable { * This class is made immutable before being delivered to an AccessibilityService. * </p> * + * @deprecated Use {@link #addLabeledBy(View)} on the labeled node instead, + * since {@link #getLabeledByList()} and {@link #getLabeledBy()} on the + * labeled node are not automatically populated when this method is used. + * * @param root The root whose virtual descendant serves as a label. * @param virtualDescendantId The id of the virtual descendant. */ + @FlaggedApi(Flags.FLAG_DEPRECATE_ANI_LABEL_FOR_APIS) + @Deprecated public void setLabelFor(View root, int virtualDescendantId) { enforceNotSealed(); final int rootAccessibilityViewId = (root != null) @@ -3902,8 +3914,14 @@ public class AccessibilityNodeInfo implements Parcelable { * Gets the node info for which the view represented by this info serves as * a label for accessibility purposes. * + * @deprecated Use {@link #getLabeledByList()} on the labeled node instead, + * since calling {@link #addLabeledBy(View)} or {@link #addLabeledBy(View, int)} + * on the labeled node do not automatically provide that node from this method. + * * @return The labeled info. */ + @FlaggedApi(Flags.FLAG_DEPRECATE_ANI_LABEL_FOR_APIS) + @Deprecated public AccessibilityNodeInfo getLabelFor() { enforceSealed(); return getNodeForAccessibilityId(mConnectionId, mWindowId, mLabelForId); diff --git a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig index 7177ef330f06..8a006fa5b509 100644 --- a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig +++ b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig @@ -92,6 +92,13 @@ flag { flag { namespace: "accessibility" + name: "deprecate_ani_label_for_apis" + description: "Controls the deprecation of AccessibilityNodeInfo labelFor apis" + bug: "333783827" +} + +flag { + namespace: "accessibility" name: "fix_merged_content_change_event_v2" description: "Fixes event type and source of content change event merged in ViewRootImpl" bug: "277305460" diff --git a/core/java/android/view/autofill/AutofillFeatureFlags.java b/core/java/android/view/autofill/AutofillFeatureFlags.java index 0ab51e45a951..905f350ca6c5 100644 --- a/core/java/android/view/autofill/AutofillFeatureFlags.java +++ b/core/java/android/view/autofill/AutofillFeatureFlags.java @@ -316,6 +316,35 @@ public class AutofillFeatureFlags { // END AUTOFILL PCC CLASSIFICATION FLAGS + // START AUTOFILL REMOVE PRE_TRIGGER FLAGS + + /** + * Whether pre-trigger flow is disabled. + * + * @hide + */ + public static final String DEVICE_CONFIG_IMPROVE_FILL_DIALOG_ENABLED = "improve_fill_dialog"; + + /** + * Minimum amount of time (in milliseconds) to wait after IME animation finishes, and before + * starting fill dialog animation. + * + * @hide + */ + public static final String DEVICE_CONFIG_FILL_DIALOG_MIN_WAIT_AFTER_IME_ANIMATION_END_MS = + "fill_dialog_min_wait_after_animation_end_ms"; + + /** + * Sets a value of timeout in milliseconds, measured after animation end, during which fill + * dialog can be shown. If we are at time > animation_end_time + this timeout, fill dialog + * wouldn't be shown. + * + * @hide + */ + public static final String DEVICE_CONFIG_FILL_DIALOG_TIMEOUT_MS = "fill_dialog_timeout_ms"; + + // END AUTOFILL REMOVE PRE_TRIGGER FLAGS + /** * Define the max input length for autofill to show suggesiton UI * @@ -366,6 +395,17 @@ public class AutofillFeatureFlags { DEFAULT_AFAA_SHOULD_INCLUDE_ALL_AUTOFILL_TYPE_NOT_NONE_VIEWS_IN_ASSIST_STRUCTURE = true; // END AUTOFILL FOR ALL APPS DEFAULTS + // START AUTOFILL REMOVE PRE_TRIGGER FLAGS DEFAULTS + // Default for whether the pre trigger removal is enabled. + /** @hide */ + public static final boolean DEFAULT_IMPROVE_FILL_DIALOG_ENABLED = true; + // Default for whether the pre trigger removal is enabled. + /** @hide */ + public static final long DEFAULT_FILL_DIALOG_TIMEOUT_MS = 300; // 300 ms + /** @hide */ + public static final long DEFAULT_FILL_DIALOG_MIN_WAIT_AFTER_IME_ANIMATION_END_MS = 0; // 0 ms + // END AUTOFILL REMOVE PRE_TRIGGER FLAGS DEFAULTS + /** * @hide */ @@ -611,4 +651,48 @@ public class AutofillFeatureFlags { } // END AUTOFILL PCC CLASSIFICATION FUNCTIONS + + + // START AUTOFILL REMOVE PRE_TRIGGER + /** + * Whether Autofill Pre Trigger Removal is enabled. + * + * @hide + */ + public static boolean isImproveFillDialogEnabled() { + // TODO(b/266379948): Add condition for checking whether device has PCC first + + return DeviceConfig.getBoolean( + DeviceConfig.NAMESPACE_AUTOFILL, + DEVICE_CONFIG_IMPROVE_FILL_DIALOG_ENABLED, + DEFAULT_IMPROVE_FILL_DIALOG_ENABLED); + } + + /** + * Whether Autofill Pre Trigger Removal is enabled. + * + * @hide + */ + public static long getFillDialogTimeoutMs() { + // TODO(b/266379948): Add condition for checking whether device has PCC first + + return DeviceConfig.getLong( + DeviceConfig.NAMESPACE_AUTOFILL, + DEVICE_CONFIG_FILL_DIALOG_TIMEOUT_MS, + DEFAULT_FILL_DIALOG_TIMEOUT_MS); + } + + /** + * Whether Autofill Pre Trigger Removal is enabled. + * + * @hide + */ + public static long getFillDialogMinWaitAfterImeAnimationtEndMs() { + // TODO(b/266379948): Add condition for checking whether device has PCC first + + return DeviceConfig.getLong( + DeviceConfig.NAMESPACE_AUTOFILL, + DEVICE_CONFIG_FILL_DIALOG_MIN_WAIT_AFTER_IME_ANIMATION_END_MS, + DEFAULT_FILL_DIALOG_MIN_WAIT_AFTER_IME_ANIMATION_END_MS); + } } diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index 1a45939f65b6..52c5af8889ec 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -25,12 +25,14 @@ import static android.service.autofill.FillRequest.FLAG_SCREEN_HAS_CREDMAN_FIELD import static android.service.autofill.FillRequest.FLAG_SUPPORTS_FILL_DIALOG; import static android.service.autofill.FillRequest.FLAG_VIEW_NOT_FOCUSED; import static android.service.autofill.FillRequest.FLAG_VIEW_REQUESTS_CREDMAN_SERVICE; +import static android.service.autofill.Flags.FLAG_FILL_DIALOG_IMPROVEMENTS; import static android.view.ContentInfo.SOURCE_AUTOFILL; import static android.view.autofill.Helper.sDebug; import static android.view.autofill.Helper.sVerbose; import static android.view.autofill.Helper.toList; import android.accessibilityservice.AccessibilityServiceInfo; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -1607,7 +1609,12 @@ public final class AutofillManager { * the virtual view in the host view. * * @throws IllegalArgumentException if the {@code infos} was empty + * + * @deprecated This function will not do anything. Showing fill dialog is now fully controlled + * by the framework and the autofill provider. */ + @FlaggedApi(FLAG_FILL_DIALOG_IMPROVEMENTS) + @Deprecated public void notifyVirtualViewsReady( @NonNull View view, @NonNull SparseArray<VirtualViewFillInfo> infos) { Objects.requireNonNull(infos); @@ -4034,8 +4041,13 @@ public final class AutofillManager { * receiving a focus event. The autofill suggestions shown will include content for * related views as well. * @return {@code true} if the autofill dialog is being shown + * + * @deprecated This function will not do anything. Showing fill dialog is now fully controlled + * by the framework and the autofill provider. */ // TODO(b/210926084): Consider whether to include the one-time show logic within this method. + @FlaggedApi(FLAG_FILL_DIALOG_IMPROVEMENTS) + @Deprecated public boolean showAutofillDialog(@NonNull View view) { Objects.requireNonNull(view); if (shouldShowAutofillDialog(view, view.getAutofillId())) { @@ -4073,7 +4085,12 @@ public final class AutofillManager { * suggestions. * @param virtualId id identifying the virtual view inside the host view. * @return {@code true} if the autofill dialog is being shown + * + * @deprecated This function will not do anything. Showing fill dialog is now fully controlled + * by the framework and the autofill provider. */ + @FlaggedApi(FLAG_FILL_DIALOG_IMPROVEMENTS) + @Deprecated public boolean showAutofillDialog(@NonNull View view, int virtualId) { Objects.requireNonNull(view); if (shouldShowAutofillDialog(view, getAutofillId(view, virtualId))) { diff --git a/core/java/android/view/flags/scroll_feedback_flags.aconfig b/core/java/android/view/flags/scroll_feedback_flags.aconfig index 658aa29a3cc6..b180e58cbe49 100644 --- a/core/java/android/view/flags/scroll_feedback_flags.aconfig +++ b/core/java/android/view/flags/scroll_feedback_flags.aconfig @@ -23,3 +23,10 @@ flag { bug: "331830899" is_fixed_read_only: true } + +flag { + namespace: "wear_frameworks" + name: "dynamic_view_rotary_haptics_configuration" + description: "Whether ScrollFeedbackProvider dynamically disables View-based rotary haptics." + bug: "377998870 " +} diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 73f9d9fc23dc..6026e60e5b59 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -2471,6 +2471,11 @@ public final class InputMethodManager { return; } + if (Flags.refactorInsetsController()) { + showSoftInput(rootView, statsToken, flags, resultReceiver, reason); + return; + } + ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_CLIENT_VIEW_SERVED); // Makes sure to call ImeInsetsSourceConsumer#onShowRequested on the UI thread. diff --git a/core/java/android/view/inputmethod/flags.aconfig b/core/java/android/view/inputmethod/flags.aconfig index aa4927ee9b9c..edd9d6cff799 100644 --- a/core/java/android/view/inputmethod/flags.aconfig +++ b/core/java/android/view/inputmethod/flags.aconfig @@ -158,3 +158,11 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "writing_tools" + namespace: "input_method" + description: "Writing tools API" + bug: "373788889" + is_fixed_read_only: true +}
\ No newline at end of file diff --git a/core/java/android/webkit/WebChromeClient.java b/core/java/android/webkit/WebChromeClient.java index 877fa74138fe..1baf3b82ca50 100644 --- a/core/java/android/webkit/WebChromeClient.java +++ b/core/java/android/webkit/WebChromeClient.java @@ -20,6 +20,8 @@ import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.Nullable; import android.annotation.SystemApi; +import android.compat.annotation.ChangeId; +import android.compat.annotation.EnabledAfter; import android.content.Intent; import android.content.pm.ActivityInfo; import android.graphics.Bitmap; @@ -552,6 +554,23 @@ public class WebChromeClient { * Parameters used in the {@link #onShowFileChooser} method. */ public static abstract class FileChooserParams { + /** + * Enable File System Access for webview. + * + * File System Access JS APIs window.showOpenFileChooser(), showDirectoryChooser(), and + * showSaveFilePicker() will invoke #onFileChooser(). Additional MODE_OPEN_FOLDER will be + * returned by #getMode(), #getIntent() will return ACTION_OPEN_DOCUMENT, + * ACTION_OPEN_DOCUMENT_TREE, and ACTION_CREATE_DOCUMENT rather than the current + * ACTION_GET_CONTENT, and new function #getPermissionMode() will be available. + * + * @hide + */ + @ChangeId + @EnabledAfter(targetSdkVersion = android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM) + @FlaggedApi(android.webkit.Flags.FLAG_FILE_SYSTEM_ACCESS) + @SystemApi + public static final long ENABLE_FILE_SYSTEM_ACCESS = 364980165L; + /** @hide */ @IntDef(prefix = { "MODE_" }, value = { MODE_OPEN, diff --git a/core/java/android/window/DesktopModeFlags.java b/core/java/android/window/DesktopModeFlags.java index dae87ddcb1bd..7a01ad340c56 100644 --- a/core/java/android/window/DesktopModeFlags.java +++ b/core/java/android/window/DesktopModeFlags.java @@ -54,6 +54,7 @@ public enum DesktopModeFlags { Flags::enableDesktopWindowingWallpaperActivity, true), ENABLE_DESKTOP_WINDOWING_MODALS_POLICY(Flags::enableDesktopWindowingModalsPolicy, true), ENABLE_THEMED_APP_HEADERS(Flags::enableThemedAppHeaders, true), + ENABLE_HOLD_TO_DRAG_APP_HANDLE(Flags::enableHoldToDragAppHandle, true), ENABLE_DESKTOP_WINDOWING_QUICK_SWITCH(Flags::enableDesktopWindowingQuickSwitch, true), ENABLE_APP_HEADER_WITH_TASK_DENSITY(Flags::enableAppHeaderWithTaskDensity, true), ENABLE_TASK_STACK_OBSERVER_IN_SHELL(Flags::enableTaskStackObserverInShell, true), @@ -75,7 +76,8 @@ public enum DesktopModeFlags { Flags::enableDesktopAppLaunchAlttabTransitions, false), ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS( Flags::enableDesktopAppLaunchTransitions, false), - ENABLE_DESKTOP_WINDOWING_PERSISTENCE(Flags::enableDesktopWindowingPersistence, false); + ENABLE_DESKTOP_WINDOWING_PERSISTENCE(Flags::enableDesktopWindowingPersistence, false), + ENABLE_HANDLE_INPUT_FIX(Flags::enableHandleInputFix, true); private static final String TAG = "DesktopModeFlagsUtil"; // Function called to obtain aconfig flag value. diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig index d39ecabbb2d2..f474b34ac390 100644 --- a/core/java/android/window/flags/lse_desktop_experience.aconfig +++ b/core/java/android/window/flags/lse_desktop_experience.aconfig @@ -353,6 +353,16 @@ flag { } flag { + name: "enable_desktop_system_dialogs_transitions" + namespace: "lse_desktop_experience" + description: "Enables custom transitions for system dialogs in Desktop Mode." + bug: "335638193" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { name: "enable_move_to_next_display_shortcut" namespace: "lse_desktop_experience" description: "Add new keyboard shortcut of moving a task into next display" diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig index 68e78fed29c5..4f924a82c9cc 100644 --- a/core/java/android/window/flags/windowing_frontend.aconfig +++ b/core/java/android/window/flags/windowing_frontend.aconfig @@ -160,12 +160,21 @@ flag { } flag { - name: "delegate_unhandled_drags" - is_exported: true - namespace: "multitasking" - description: "Enables delegating unhandled drags to SystemUI" - bug: "320797628" - is_fixed_read_only: true + name: "delegate_unhandled_drags" + is_exported: true + namespace: "multitasking" + description: "Enables delegating unhandled drags to SystemUI" + bug: "320797628" + is_fixed_read_only: true +} + +flag { + name: "supports_drag_assistant_to_multiwindow" + is_exported: true + namespace: "multitasking" + description: "Enables support for dragging the assistant into multiwindow" + bug: "371206207" + is_fixed_read_only: true } flag { @@ -268,6 +277,16 @@ flag { } flag { + name: "system_ui_post_animation_end" + namespace: "windowing_frontend" + description: "Run AnimatorListener#onAnimationEnd on next frame for SystemUI" + bug: "300035126" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { name: "system_ui_immersive_confirmation_dialog" namespace: "windowing_frontend" description: "Enable the implementation of the immersive confirmation dialog on system UI side by default" diff --git a/core/java/com/android/internal/accessibility/common/ShortcutConstants.java b/core/java/com/android/internal/accessibility/common/ShortcutConstants.java index 44dceb9b7edb..4a49bb6720ef 100644 --- a/core/java/com/android/internal/accessibility/common/ShortcutConstants.java +++ b/core/java/com/android/internal/accessibility/common/ShortcutConstants.java @@ -63,6 +63,8 @@ public final class ShortcutConstants { * quickly tapping screen 2 times with two fingers as preferred shortcut. * {@code QUICK_SETTINGS} for displaying specifying the accessibility services or features which * choose Quick Settings as preferred shortcut. + * {@code KEY_GESTURE} for shortcuts which are directly from key gestures and should be + * activated always. */ @Retention(RetentionPolicy.SOURCE) @IntDef({ @@ -73,6 +75,7 @@ public final class ShortcutConstants { UserShortcutType.TWOFINGER_DOUBLETAP, UserShortcutType.QUICK_SETTINGS, UserShortcutType.GESTURE, + UserShortcutType.KEY_GESTURE, UserShortcutType.ALL }) public @interface UserShortcutType { @@ -84,8 +87,10 @@ public final class ShortcutConstants { int TWOFINGER_DOUBLETAP = 1 << 3; int QUICK_SETTINGS = 1 << 4; int GESTURE = 1 << 5; + int KEY_GESTURE = 1 << 6; // LINT.ThenChange(:shortcut_type_array) - int ALL = SOFTWARE | HARDWARE | TRIPLETAP | TWOFINGER_DOUBLETAP | QUICK_SETTINGS | GESTURE; + int ALL = SOFTWARE | HARDWARE | TRIPLETAP | TWOFINGER_DOUBLETAP | QUICK_SETTINGS | GESTURE + | KEY_GESTURE; } /** @@ -99,7 +104,8 @@ public final class ShortcutConstants { UserShortcutType.TRIPLETAP, UserShortcutType.TWOFINGER_DOUBLETAP, UserShortcutType.QUICK_SETTINGS, - UserShortcutType.GESTURE + UserShortcutType.GESTURE, + UserShortcutType.KEY_GESTURE // LINT.ThenChange(:shortcut_type_intdef) }; diff --git a/core/java/com/android/internal/accessibility/util/ShortcutUtils.java b/core/java/com/android/internal/accessibility/util/ShortcutUtils.java index 2e0ff3db6c50..14ca0f8cae69 100644 --- a/core/java/com/android/internal/accessibility/util/ShortcutUtils.java +++ b/core/java/com/android/internal/accessibility/util/ShortcutUtils.java @@ -27,6 +27,7 @@ import static com.android.internal.accessibility.common.ShortcutConstants.SERVIC import static com.android.internal.accessibility.common.ShortcutConstants.USER_SHORTCUT_TYPES; 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.KEY_GESTURE; import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.QUICK_SETTINGS; import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE; import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.TRIPLETAP; @@ -187,6 +188,7 @@ public final class ShortcutUtils { case TWOFINGER_DOUBLETAP -> Settings.Secure.ACCESSIBILITY_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP_ENABLED; case QUICK_SETTINGS -> Settings.Secure.ACCESSIBILITY_QS_TARGETS; + case KEY_GESTURE -> Settings.Secure.ACCESSIBILITY_KEY_GESTURE_TARGETS; default -> throw new IllegalArgumentException( "Unsupported user shortcut type: " + type); }; @@ -209,6 +211,7 @@ public final class ShortcutUtils { TRIPLETAP; case Settings.Secure.ACCESSIBILITY_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP_ENABLED -> TWOFINGER_DOUBLETAP; + case Settings.Secure.ACCESSIBILITY_KEY_GESTURE_TARGETS -> KEY_GESTURE; default -> throw new IllegalArgumentException( "Unsupported user shortcut key: " + key); }; diff --git a/core/java/com/android/internal/app/AppLocaleCollector.java b/core/java/com/android/internal/app/AppLocaleCollector.java index 56f633fbc6c9..ca1fc0a6d41f 100644 --- a/core/java/com/android/internal/app/AppLocaleCollector.java +++ b/core/java/com/android/internal/app/AppLocaleCollector.java @@ -41,7 +41,7 @@ import java.util.Set; import java.util.stream.Collectors; /** The Locale data collector for per-app language. */ -public class AppLocaleCollector implements LocalePickerWithRegion.LocaleCollectorBase { +public class AppLocaleCollector implements LocaleCollectorBase { private static final String TAG = AppLocaleCollector.class.getSimpleName(); private final Context mContext; private final String mAppPackageName; @@ -167,8 +167,8 @@ public class AppLocaleCollector implements LocalePickerWithRegion.LocaleCollecto } @Override - public HashSet<String> getIgnoredLocaleList(boolean translatedOnly) { - HashSet<String> langTagsToIgnore = new HashSet<>(); + public Set<String> getIgnoredLocaleList(boolean translatedOnly) { + Set<String> langTagsToIgnore = new HashSet<>(); if (mAppCurrentLocale != null) { langTagsToIgnore.add(mAppCurrentLocale.getLocale().toLanguageTag()); diff --git a/core/java/com/android/internal/app/LocaleCollectorBase.java b/core/java/com/android/internal/app/LocaleCollectorBase.java new file mode 100644 index 000000000000..f83907771074 --- /dev/null +++ b/core/java/com/android/internal/app/LocaleCollectorBase.java @@ -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.internal.app; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Set; + +/** + * The interface which provides the locale list. + */ +public interface LocaleCollectorBase { + + /** Gets the ignored locale list. */ + Set<String> getIgnoredLocaleList(boolean translatedOnly); + + /** Gets the supported locale list. */ + Set<LocaleStore.LocaleInfo> getSupportedLocaleList(LocaleStore.LocaleInfo parent, + boolean translatedOnly, boolean isForCountryMode); + + /** Indicates if the class work for specific package. */ + boolean hasSpecificPackageName(); +} diff --git a/core/java/com/android/internal/app/LocalePickerWithRegion.java b/core/java/com/android/internal/app/LocalePickerWithRegion.java index ef4acd1cdfcb..ffffefa64758 100644 --- a/core/java/com/android/internal/app/LocalePickerWithRegion.java +++ b/core/java/com/android/internal/app/LocalePickerWithRegion.java @@ -44,7 +44,10 @@ import java.util.Set; * <p>It shows suggestions at the top, then the rest of the locales. * Allows the user to search for locales using both their native name and their name in the * default locale.</p> + * + * @deprecated use SettingLib's widget instead of customized UIs. */ +@Deprecated public class LocalePickerWithRegion extends ListFragment implements SearchView.OnQueryTextListener { private static final String TAG = LocalePickerWithRegion.class.getSimpleName(); private static final String PARENT_FRAGMENT_NAME = "localeListEditor"; @@ -78,21 +81,6 @@ public class LocalePickerWithRegion extends ListFragment implements SearchView.O default void onParentLocaleSelected(LocaleStore.LocaleInfo locale) {} } - /** - * The interface which provides the locale list. - */ - interface LocaleCollectorBase { - /** Gets the ignored locale list. */ - HashSet<String> getIgnoredLocaleList(boolean translatedOnly); - - /** Gets the supported locale list. */ - Set<LocaleStore.LocaleInfo> getSupportedLocaleList(LocaleStore.LocaleInfo parent, - boolean translatedOnly, boolean isForCountryMode); - - /** Indicates if the class work for specific package. */ - boolean hasSpecificPackageName(); - } - private static LocalePickerWithRegion createNumberingSystemPicker( LocaleSelectedListener listener, LocaleStore.LocaleInfo parent, boolean translatedOnly, OnActionExpandListener onActionExpandListener, diff --git a/core/java/com/android/internal/app/SystemLocaleCollector.java b/core/java/com/android/internal/app/SystemLocaleCollector.java index 416f510b3230..c7931cbfcc5d 100644 --- a/core/java/com/android/internal/app/SystemLocaleCollector.java +++ b/core/java/com/android/internal/app/SystemLocaleCollector.java @@ -24,7 +24,7 @@ import java.util.HashSet; import java.util.Set; /** The Locale data collector for System language. */ -class SystemLocaleCollector implements LocalePickerWithRegion.LocaleCollectorBase { +public class SystemLocaleCollector implements LocaleCollectorBase { private final Context mContext; private LocaleList mExplicitLocales; @@ -32,14 +32,14 @@ class SystemLocaleCollector implements LocalePickerWithRegion.LocaleCollectorBas this(context, null); } - SystemLocaleCollector(Context context, LocaleList explicitLocales) { + public SystemLocaleCollector(Context context, LocaleList explicitLocales) { mContext = context; mExplicitLocales = explicitLocales; } @Override - public HashSet<String> getIgnoredLocaleList(boolean translatedOnly) { - HashSet<String> ignoreList = new HashSet<>(); + public Set<String> getIgnoredLocaleList(boolean translatedOnly) { + Set<String> ignoreList = new HashSet<>(); if (!translatedOnly) { final LocaleList userLocales = LocalePicker.getLocales(); final String[] langTags = userLocales.toLanguageTags().split(","); diff --git a/core/java/com/android/internal/os/LongArrayMultiStateCounter.java b/core/java/com/android/internal/os/LongArrayMultiStateCounter.java index b3480ab92f46..2931bd2c83dd 100644 --- a/core/java/com/android/internal/os/LongArrayMultiStateCounter.java +++ b/core/java/com/android/internal/os/LongArrayMultiStateCounter.java @@ -56,7 +56,7 @@ import libcore.util.NativeAllocationRegistry; * @hide */ @RavenwoodKeepWholeClass -@RavenwoodRedirectionClass("LongArrayMultiStateCounter_host") +@RavenwoodRedirectionClass("LongArrayMultiStateCounter_ravenwood") public final class LongArrayMultiStateCounter implements Parcelable { private static volatile NativeAllocationRegistry sRegistry; private final int mStateCount; diff --git a/ravenwood/runtime-helper-src/framework/com/android/internal/os/LongArrayMultiStateCounter_host.java b/core/java/com/android/internal/os/LongArrayMultiStateCounter_ravenwood.java index 90608f6e87d1..7030d8e84b70 100644 --- a/ravenwood/runtime-helper-src/framework/com/android/internal/os/LongArrayMultiStateCounter_host.java +++ b/core/java/com/android/internal/os/LongArrayMultiStateCounter_ravenwood.java @@ -18,6 +18,7 @@ package com.android.internal.os; import android.os.BadParcelableException; import android.os.Parcel; +import android.ravenwood.annotation.RavenwoodKeepWholeClass; import java.util.Arrays; import java.util.HashMap; @@ -25,7 +26,8 @@ import java.util.HashMap; /** * Native implementation substitutions for the LongArrayMultiStateCounter class. */ -public class LongArrayMultiStateCounter_host { +@RavenwoodKeepWholeClass +class LongArrayMultiStateCounter_ravenwood { /** * A reimplementation of {@link LongArrayMultiStateCounter}, only in diff --git a/core/java/com/android/internal/os/LongMultiStateCounter.java b/core/java/com/android/internal/os/LongMultiStateCounter.java index c386a86f5906..ee855d58c874 100644 --- a/core/java/com/android/internal/os/LongMultiStateCounter.java +++ b/core/java/com/android/internal/os/LongMultiStateCounter.java @@ -60,7 +60,7 @@ import libcore.util.NativeAllocationRegistry; * @hide */ @RavenwoodKeepWholeClass -@RavenwoodRedirectionClass("LongMultiStateCounter_host") +@RavenwoodRedirectionClass("LongMultiStateCounter_ravenwood") public final class LongMultiStateCounter implements Parcelable { private static NativeAllocationRegistry sRegistry; diff --git a/ravenwood/runtime-helper-src/framework/com/android/internal/os/LongMultiStateCounter_host.java b/core/java/com/android/internal/os/LongMultiStateCounter_ravenwood.java index 1d95aa143549..42db37f1f82f 100644 --- a/ravenwood/runtime-helper-src/framework/com/android/internal/os/LongMultiStateCounter_host.java +++ b/core/java/com/android/internal/os/LongMultiStateCounter_ravenwood.java @@ -18,13 +18,15 @@ package com.android.internal.os; import android.os.BadParcelableException; import android.os.Parcel; +import android.ravenwood.annotation.RavenwoodKeepWholeClass; import java.util.HashMap; /** * Native implementation substitutions for the LongMultiStateCounter class. */ -public class LongMultiStateCounter_host { +@RavenwoodKeepWholeClass +class LongMultiStateCounter_ravenwood { /** * A reimplementation of {@link com.android.internal.os.LongMultiStateCounter}, only in diff --git a/core/java/com/android/internal/pm/pkg/component/AconfigFlags.java b/core/java/com/android/internal/pm/pkg/component/AconfigFlags.java index 70b7953ed364..c953d88c9482 100644 --- a/core/java/com/android/internal/pm/pkg/component/AconfigFlags.java +++ b/core/java/com/android/internal/pm/pkg/component/AconfigFlags.java @@ -214,13 +214,30 @@ public class AconfigFlags { * @param parser XML parser object currently parsing an element * @return true if the element is disabled because of its feature flag */ + public boolean skipCurrentElement(@Nullable ParsingPackage pkg, @NonNull XmlPullParser parser) { + return skipCurrentElement(pkg, parser, /* allowNoNamespace= */ false); + } + + /** + * Check if the element in {@code parser} should be skipped because of the feature flag. + * @param pkg The package being parsed + * @param parser XML parser object currently parsing an element + * @param allowNoNamespace Whether to allow namespace null + * @return true if the element is disabled because of its feature flag + */ public boolean skipCurrentElement( - @NonNull ParsingPackage pkg, - @NonNull XmlResourceParser parser) { + @Nullable ParsingPackage pkg, + @NonNull XmlPullParser parser, + boolean allowNoNamespace + ) { if (!Flags.manifestFlagging()) { return false; } String featureFlag = parser.getAttributeValue(ANDROID_RES_NAMESPACE, "featureFlag"); + // If allow no namespace, make another attempt to parse feature flag with null namespace. + if (featureFlag == null && allowNoNamespace) { + featureFlag = parser.getAttributeValue(null, "featureFlag"); + } if (featureFlag == null) { return false; } @@ -242,7 +259,7 @@ public class AconfigFlags { + " behind feature flag " + featureFlag + " = " + flagValue); shouldSkip = true; } - if (android.content.pm.Flags.includeFeatureFlagsInPackageCacher()) { + if (pkg != null && android.content.pm.Flags.includeFeatureFlagsInPackageCacher()) { pkg.addFeatureFlag(featureFlag, flagValue); } return shouldSkip; diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java index bd746d5ecf04..270cf085b06f 100644 --- a/core/java/com/android/internal/policy/DecorView.java +++ b/core/java/com/android/internal/policy/DecorView.java @@ -46,9 +46,13 @@ import static com.android.internal.policy.PhoneWindow.FEATURE_OPTIONS_PANEL; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; +import android.annotation.FlaggedApi; +import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.TestApi; import android.app.WindowConfiguration; +import android.app.jank.AppJankStats; +import android.app.jank.JankTracker; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.res.Configuration; @@ -283,6 +287,9 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind private final WearGestureInterceptionDetector mWearGestureInterceptionDetector; + @Nullable + private AppJankStatsCallback mAppJankStatsCallback; + DecorView(Context context, int featureId, PhoneWindow window, WindowManager.LayoutParams params) { super(context); @@ -2336,6 +2343,38 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind } } + public interface AppJankStatsCallback { + /** + * Called when app jank stats are being reported to the platform or when a widget needs + * to obtain a reference to the JankTracker instance to update states. + */ + JankTracker getAppJankTracker(); + } + + public void setAppJankStatsCallback(AppJankStatsCallback + jankStatsReportedCallback) { + mAppJankStatsCallback = jankStatsReportedCallback; + } + + @Override + @FlaggedApi(android.app.jank.Flags.FLAG_DETAILED_APP_JANK_METRICS_API) + public void reportAppJankStats(@NonNull AppJankStats appJankStats) { + if (mAppJankStatsCallback != null) { + JankTracker jankTracker = mAppJankStatsCallback.getAppJankTracker(); + if (jankTracker != null) { + jankTracker.mergeAppJankStats(appJankStats); + } + } + } + + @Override + public @Nullable JankTracker getJankTracker() { + if (mAppJankStatsCallback != null) { + return mAppJankStatsCallback.getAppJankTracker(); + } + return null; + } + @Override public String toString() { return super.toString() + "[" + getTitleSuffix(mWindow.getAttributes()) + "]"; diff --git a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl index b5c87868af12..0e85e046e1b6 100644 --- a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl +++ b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl @@ -27,6 +27,7 @@ import android.telephony.PhoneCapability; import android.telephony.PhysicalChannelConfig; import android.telephony.PreciseCallState; import android.telephony.PreciseDataConnectionState; +import android.telephony.satellite.NtnSignalStrength; import android.telephony.ServiceState; import android.telephony.SignalStrength; import android.telephony.emergency.EmergencyNumber; @@ -85,4 +86,5 @@ oneway interface IPhoneStateListener { void onCarrierRoamingNtnModeChanged(in boolean active); void onCarrierRoamingNtnEligibleStateChanged(in boolean eligible); void onCarrierRoamingNtnAvailableServicesChanged(in int[] availableServices); + void onCarrierRoamingNtnSignalStrengthChanged(in NtnSignalStrength ntnSignalStrength); } diff --git a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl index 1c76a6cd4bba..0f268d5de62b 100644 --- a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl +++ b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl @@ -29,6 +29,7 @@ import android.telephony.ims.ImsReasonInfo; import android.telephony.PhoneCapability; import android.telephony.PhysicalChannelConfig; import android.telephony.PreciseDataConnectionState; +import android.telephony.satellite.NtnSignalStrength; import android.telephony.ServiceState; import android.telephony.SignalStrength; import android.telephony.emergency.EmergencyNumber; @@ -125,8 +126,10 @@ interface ITelephonyRegistry { void notifyCarrierRoamingNtnModeChanged(int subId, in boolean active); void notifyCarrierRoamingNtnEligibleStateChanged(int subId, in boolean eligible); void notifyCarrierRoamingNtnAvailableServicesChanged(int subId, in int[] availableServices); + void notifyCarrierRoamingNtnSignalStrengthChanged(int subId, in NtnSignalStrength ntnSignalStrength); void addSatelliteStateChangeListener(ISatelliteStateChangeListener listener, String pkg, String featureId); void removeSatelliteStateChangeListener(ISatelliteStateChangeListener listener, String pkg); void notifySatelliteStateChanged(boolean isEnabled); + } diff --git a/core/java/com/android/internal/widget/NotificationProgressDrawable.java b/core/java/com/android/internal/widget/NotificationProgressDrawable.java index 30deb499594c..fb6937c94a3e 100644 --- a/core/java/com/android/internal/widget/NotificationProgressDrawable.java +++ b/core/java/com/android/internal/widget/NotificationProgressDrawable.java @@ -63,6 +63,7 @@ public final class NotificationProgressDrawable extends Drawable { private final ArrayList<Part> mParts = new ArrayList<>(); + private final RectF mSegRectF = new RectF(); private final Rect mPointRect = new Rect(); private final RectF mPointRectF = new RectF(); @@ -198,22 +199,42 @@ public final class NotificationProgressDrawable extends Drawable { mState.mSegSegGap, x + segWidth, totalWidth); final float end = x + segWidth - endOffset; - // Transparent is not allowed (and also is the default in the data), so use that - // as a sentinel to be replaced by default - mStrokePaint.setColor(segment.mColor != Color.TRANSPARENT ? segment.mColor - : mState.mStrokeColor); - mDashedStrokePaint.setColor(segment.mColor != Color.TRANSPARENT ? segment.mColor - : mState.mFadedStrokeColor); - - // Leave space for the rounded line cap which extends beyond start/end. - final float capWidth = mStrokePaint.getStrokeWidth() / 2F; - - canvas.drawLine(start + capWidth, centerY, end - capWidth, centerY, - segment.mDashed ? mDashedStrokePaint : mStrokePaint); - // Advance the current position to account for the segment's fraction of the total // width (ignoring offset and padding) x += segWidth; + + // No space left to draw the segment + if (start > end) continue; + + if (segment.mDashed) { + // No caps when the segment is dashed. + + mDashedStrokePaint.setColor(segment.mColor != Color.TRANSPARENT ? segment.mColor + : mState.mFadedStrokeColor); + canvas.drawLine(start, centerY, end, centerY, mDashedStrokePaint); + } else if (end - start < mState.mStrokeWidth) { + // Not enough segment length to draw the caps + + final float rad = (end - start) / 2F; + final float capWidth = mStrokePaint.getStrokeWidth() / 2F; + + mFillPaint.setColor(segment.mColor != Color.TRANSPARENT ? segment.mColor + : mState.mStrokeColor); + + mSegRectF.set(start, centerY - capWidth, end, centerY + capWidth); + canvas.drawRoundRect(mSegRectF, rad, rad, mFillPaint); + } else { + // Leave space for the rounded line cap which extends beyond start/end. + final float capWidth = mStrokePaint.getStrokeWidth() / 2F; + + // Transparent is not allowed (and also is the default in the data), so use that + // as a sentinel to be replaced by default + mStrokePaint.setColor(segment.mColor != Color.TRANSPARENT ? segment.mColor + : mState.mStrokeColor); + + canvas.drawLine(start + capWidth, centerY, end - capWidth, centerY, + mStrokePaint); + } } else if (part instanceof Point point) { final float pointWidth = 2 * pointRadius; float start = x - pointRadius; @@ -232,7 +253,7 @@ public final class NotificationProgressDrawable extends Drawable { } else { // TODO: b/367804171 - actually use a vector asset for the default point // rather than drawing it as a box? - mPointRectF.set(mPointRect); + mPointRectF.set(start, centerY - pointRadius, end, centerY + pointRadius); final float inset = mState.mPointRectInset; final float cornerRadius = mState.mPointRectCornerRadius; mPointRectF.inset(inset, inset); diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp index 50252c11ffb1..42406147b2f0 100644 --- a/core/jni/android_hardware_Camera.cpp +++ b/core/jni/android_hardware_Camera.cpp @@ -538,7 +538,7 @@ static bool attributionSourceStateForJavaParcel(JNIEnv *env, jobject jClientAttr return false; } - if (!(useContextAttributionSource && flags::use_context_attribution_source())) { + if (!(useContextAttributionSource && flags::data_delivery_permission_checks())) { clientAttribution.uid = Camera::USE_CALLING_UID; clientAttribution.pid = Camera::USE_CALLING_PID; } diff --git a/core/jni/android_hardware_input_InputWindowHandle.cpp b/core/jni/android_hardware_input_InputWindowHandle.cpp index 69f633420a0d..f1c4913fe006 100644 --- a/core/jni/android_hardware_input_InputWindowHandle.cpp +++ b/core/jni/android_hardware_input_InputWindowHandle.cpp @@ -83,68 +83,53 @@ static struct { jmethodID ctor; } gRegionClassInfo; -static Mutex gHandleMutex; - - -// --- NativeInputWindowHandle --- - -NativeInputWindowHandle::NativeInputWindowHandle(jweak objWeak) : - mObjWeak(objWeak) { -} - -NativeInputWindowHandle::~NativeInputWindowHandle() { - JNIEnv* env = AndroidRuntime::getJNIEnv(); - env->DeleteWeakGlobalRef(mObjWeak); +// --- Global functions --- - // Clear the weak reference to the layer handle and flush any binder ref count operations so we - // do not hold on to any binder references. - // TODO(b/139697085) remove this after it can be flushed automatically - mInfo.touchableRegionCropHandle.clear(); - IPCThreadState::self()->flushCommands(); -} +sp<gui::WindowInfoHandle> android_view_InputWindowHandle_getHandle(JNIEnv* env, jobject obj) { + sp<gui::WindowInfoHandle> handle = [&]() { + jlong cachedHandle = env->GetLongField(obj, gInputWindowHandleClassInfo.ptr); + if (cachedHandle) { + return sp<gui::WindowInfoHandle>::fromExisting( + reinterpret_cast<gui::WindowInfoHandle*>(cachedHandle)); + } -jobject NativeInputWindowHandle::getInputWindowHandleObjLocalRef(JNIEnv* env) { - return env->NewLocalRef(mObjWeak); -} + auto newHandle = sp<gui::WindowInfoHandle>::make(); + newHandle->incStrong((void*)android_view_InputWindowHandle_getHandle); + env->SetLongField(obj, gInputWindowHandleClassInfo.ptr, + reinterpret_cast<jlong>(newHandle.get())); + return newHandle; + }(); -bool NativeInputWindowHandle::updateInfo() { - JNIEnv* env = AndroidRuntime::getJNIEnv(); - jobject obj = env->NewLocalRef(mObjWeak); - if (!obj) { - releaseChannel(); - return false; - } + gui::WindowInfo* windowInfo = handle->editInfo(); - mInfo.touchableRegion.clear(); + windowInfo->touchableRegion.clear(); jobject tokenObj = env->GetObjectField(obj, gInputWindowHandleClassInfo.token); if (tokenObj) { - mInfo.token = ibinderForJavaObject(env, tokenObj); + windowInfo->token = ibinderForJavaObject(env, tokenObj); env->DeleteLocalRef(tokenObj); } else { - mInfo.token.clear(); + windowInfo->token.clear(); } - mInfo.name = getStringField(env, obj, gInputWindowHandleClassInfo.name, "<null>"); + windowInfo->name = getStringField(env, obj, gInputWindowHandleClassInfo.name, "<null>"); - mInfo.dispatchingTimeout = std::chrono::milliseconds( + windowInfo->dispatchingTimeout = std::chrono::milliseconds( env->GetLongField(obj, gInputWindowHandleClassInfo.dispatchingTimeoutMillis)); ScopedLocalRef<jobject> frameObj(env, env->GetObjectField(obj, gInputWindowHandleClassInfo.frame)); - mInfo.frame = JNICommon::rectFromObj(env, frameObj.get()); + windowInfo->frame = JNICommon::rectFromObj(env, frameObj.get()); - mInfo.surfaceInset = env->GetIntField(obj, - gInputWindowHandleClassInfo.surfaceInset); - mInfo.globalScaleFactor = env->GetFloatField(obj, - gInputWindowHandleClassInfo.scaleFactor); + windowInfo->surfaceInset = env->GetIntField(obj, gInputWindowHandleClassInfo.surfaceInset); + windowInfo->globalScaleFactor = + env->GetFloatField(obj, gInputWindowHandleClassInfo.scaleFactor); - jobject regionObj = env->GetObjectField(obj, - gInputWindowHandleClassInfo.touchableRegion); + jobject regionObj = env->GetObjectField(obj, gInputWindowHandleClassInfo.touchableRegion); if (regionObj) { for (graphics::RegionIterator it(env, regionObj); !it.isDone(); it.next()) { ARect rect = it.getRect(); - mInfo.addTouchableRegion(Rect(rect.left, rect.top, rect.right, rect.bottom)); + windowInfo->addTouchableRegion(Rect(rect.left, rect.top, rect.right, rect.bottom)); } env->DeleteLocalRef(regionObj); } @@ -153,49 +138,55 @@ bool NativeInputWindowHandle::updateInfo() { env->GetIntField(obj, gInputWindowHandleClassInfo.layoutParamsFlags)); const auto type = static_cast<WindowInfo::Type>( env->GetIntField(obj, gInputWindowHandleClassInfo.layoutParamsType)); - mInfo.layoutParamsFlags = flags; - mInfo.layoutParamsType = type; + windowInfo->layoutParamsFlags = flags; + windowInfo->layoutParamsType = type; - mInfo.inputConfig = static_cast<gui::WindowInfo::InputConfig>( + windowInfo->inputConfig = static_cast<gui::WindowInfo::InputConfig>( env->GetIntField(obj, gInputWindowHandleClassInfo.inputConfig)); - mInfo.touchOcclusionMode = static_cast<TouchOcclusionMode>( + windowInfo->touchOcclusionMode = static_cast<TouchOcclusionMode>( env->GetIntField(obj, gInputWindowHandleClassInfo.touchOcclusionMode)); - mInfo.ownerPid = gui::Pid{env->GetIntField(obj, gInputWindowHandleClassInfo.ownerPid)}; - mInfo.ownerUid = gui::Uid{ + windowInfo->ownerPid = gui::Pid{env->GetIntField(obj, gInputWindowHandleClassInfo.ownerPid)}; + windowInfo->ownerUid = gui::Uid{ static_cast<uid_t>(env->GetIntField(obj, gInputWindowHandleClassInfo.ownerUid))}; - mInfo.packageName = getStringField(env, obj, gInputWindowHandleClassInfo.packageName, "<null>"); - mInfo.displayId = + windowInfo->packageName = + getStringField(env, obj, gInputWindowHandleClassInfo.packageName, "<null>"); + windowInfo->displayId = ui::LogicalDisplayId{env->GetIntField(obj, gInputWindowHandleClassInfo.displayId)}; - jobject inputApplicationHandleObj = env->GetObjectField(obj, - gInputWindowHandleClassInfo.inputApplicationHandle); + jobject inputApplicationHandleObj = + env->GetObjectField(obj, gInputWindowHandleClassInfo.inputApplicationHandle); if (inputApplicationHandleObj) { std::shared_ptr<InputApplicationHandle> inputApplicationHandle = android_view_InputApplicationHandle_getHandle(env, inputApplicationHandleObj); if (inputApplicationHandle != nullptr) { inputApplicationHandle->updateInfo(); - mInfo.applicationInfo = *(inputApplicationHandle->getInfo()); + windowInfo->applicationInfo = *(inputApplicationHandle->getInfo()); } env->DeleteLocalRef(inputApplicationHandleObj); } - mInfo.replaceTouchableRegionWithCrop = env->GetBooleanField(obj, - gInputWindowHandleClassInfo.replaceTouchableRegionWithCrop); + windowInfo->replaceTouchableRegionWithCrop = + env->GetBooleanField(obj, gInputWindowHandleClassInfo.replaceTouchableRegionWithCrop); - jobject weakSurfaceCtrl = env->GetObjectField(obj, - gInputWindowHandleClassInfo.touchableRegionSurfaceControl.ctrl); + jobject weakSurfaceCtrl = + env->GetObjectField(obj, + gInputWindowHandleClassInfo.touchableRegionSurfaceControl.ctrl); bool touchableRegionCropHandleSet = false; if (weakSurfaceCtrl) { // Promote java weak reference. - jobject strongSurfaceCtrl = env->CallObjectMethod(weakSurfaceCtrl, - gInputWindowHandleClassInfo.touchableRegionSurfaceControl.get); + jobject strongSurfaceCtrl = + env->CallObjectMethod(weakSurfaceCtrl, + gInputWindowHandleClassInfo.touchableRegionSurfaceControl + .get); if (strongSurfaceCtrl) { - jlong mNativeObject = env->GetLongField(strongSurfaceCtrl, - gInputWindowHandleClassInfo.touchableRegionSurfaceControl.mNativeObject); + jlong mNativeObject = + env->GetLongField(strongSurfaceCtrl, + gInputWindowHandleClassInfo.touchableRegionSurfaceControl + .mNativeObject); if (mNativeObject) { auto ctrl = reinterpret_cast<SurfaceControl *>(mNativeObject); - mInfo.touchableRegionCropHandle = ctrl->getHandle(); + windowInfo->touchableRegionCropHandle = ctrl->getHandle(); touchableRegionCropHandleSet = true; } env->DeleteLocalRef(strongSurfaceCtrl); @@ -203,15 +194,15 @@ bool NativeInputWindowHandle::updateInfo() { env->DeleteLocalRef(weakSurfaceCtrl); } if (!touchableRegionCropHandleSet) { - mInfo.touchableRegionCropHandle.clear(); + windowInfo->touchableRegionCropHandle.clear(); } jobject windowTokenObj = env->GetObjectField(obj, gInputWindowHandleClassInfo.windowToken); if (windowTokenObj) { - mInfo.windowToken = ibinderForJavaObject(env, windowTokenObj); + windowInfo->windowToken = ibinderForJavaObject(env, windowTokenObj); env->DeleteLocalRef(windowTokenObj); } else { - mInfo.windowToken.clear(); + windowInfo->windowToken.clear(); } ScopedLocalRef<jobject> @@ -220,41 +211,16 @@ bool NativeInputWindowHandle::updateInfo() { gInputWindowHandleClassInfo .focusTransferTarget)); if (focusTransferTargetObj.get()) { - mInfo.focusTransferTarget = ibinderForJavaObject(env, focusTransferTargetObj.get()); + windowInfo->focusTransferTarget = ibinderForJavaObject(env, focusTransferTargetObj.get()); } else { - mInfo.focusTransferTarget.clear(); + windowInfo->focusTransferTarget.clear(); } - env->DeleteLocalRef(obj); - return true; -} - - -// --- Global functions --- - -sp<NativeInputWindowHandle> android_view_InputWindowHandle_getHandle( - JNIEnv* env, jobject inputWindowHandleObj) { - if (!inputWindowHandleObj) { - return NULL; - } - - AutoMutex _l(gHandleMutex); - - jlong ptr = env->GetLongField(inputWindowHandleObj, gInputWindowHandleClassInfo.ptr); - NativeInputWindowHandle* handle; - if (ptr) { - handle = reinterpret_cast<NativeInputWindowHandle*>(ptr); - } else { - jweak objWeak = env->NewWeakGlobalRef(inputWindowHandleObj); - handle = new NativeInputWindowHandle(objWeak); - handle->incStrong((void*)android_view_InputWindowHandle_getHandle); - env->SetLongField(inputWindowHandleObj, gInputWindowHandleClassInfo.ptr, - reinterpret_cast<jlong>(handle)); - } return handle; } -jobject android_view_InputWindowHandle_fromWindowInfo(JNIEnv* env, gui::WindowInfo windowInfo) { +jobject android_view_InputWindowHandle_fromWindowInfo(JNIEnv* env, + const gui::WindowInfo& windowInfo) { ScopedLocalRef<jobject> applicationHandle(env, android_view_InputApplicationHandle_fromInputApplicationInfo( @@ -337,18 +303,15 @@ jobject android_view_InputWindowHandle_fromWindowInfo(JNIEnv* env, gui::WindowIn // --- JNI --- static void android_view_InputWindowHandle_nativeDispose(JNIEnv* env, jobject obj) { - AutoMutex _l(gHandleMutex); - jlong ptr = env->GetLongField(obj, gInputWindowHandleClassInfo.ptr); - if (ptr) { - env->SetLongField(obj, gInputWindowHandleClassInfo.ptr, 0); - - NativeInputWindowHandle* handle = reinterpret_cast<NativeInputWindowHandle*>(ptr); - handle->decStrong((void*)android_view_InputWindowHandle_getHandle); + if (!ptr) { + return; } + env->SetLongField(obj, gInputWindowHandleClassInfo.ptr, 0); + auto handle = reinterpret_cast<gui::WindowInfoHandle*>(ptr); + handle->decStrong((void*)android_view_InputWindowHandle_getHandle); } - static const JNINativeMethod gInputWindowHandleMethods[] = { /* name, signature, funcPtr */ { "nativeDispose", "()V", diff --git a/core/jni/android_hardware_input_InputWindowHandle.h b/core/jni/android_hardware_input_InputWindowHandle.h index 408e0f1bfa36..aa375e9ef477 100644 --- a/core/jni/android_hardware_input_InputWindowHandle.h +++ b/core/jni/android_hardware_input_InputWindowHandle.h @@ -24,24 +24,11 @@ namespace android { -class NativeInputWindowHandle : public gui::WindowInfoHandle { -public: - NativeInputWindowHandle(jweak objWeak); - virtual ~NativeInputWindowHandle(); +sp<gui::WindowInfoHandle> android_view_InputWindowHandle_getHandle(JNIEnv* env, + jobject inputWindowHandleObj); - jobject getInputWindowHandleObjLocalRef(JNIEnv* env); - - virtual bool updateInfo(); - -private: - jweak mObjWeak; -}; - -extern sp<NativeInputWindowHandle> android_view_InputWindowHandle_getHandle( - JNIEnv* env, jobject inputWindowHandleObj); - -extern jobject android_view_InputWindowHandle_fromWindowInfo(JNIEnv* env, - gui::WindowInfo windowInfo); +jobject android_view_InputWindowHandle_fromWindowInfo(JNIEnv* env, + const gui::WindowInfo& windowInfo); } // namespace android diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index 56292c3d0fb2..d3bf36e60345 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -979,14 +979,16 @@ static void nativeSetAlpha(JNIEnv* env, jclass clazz, jlong transactionObj, static void nativeSetInputWindowInfo(JNIEnv* env, jclass clazz, jlong transactionObj, jlong nativeObject, jobject inputWindow) { - auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj); + if (!inputWindow) { + jniThrowNullPointerException(env, "InputWindowHandle is null"); + return; + } - sp<NativeInputWindowHandle> handle = android_view_InputWindowHandle_getHandle( - env, inputWindow); - handle->updateInfo(); + auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj); + sp<gui::WindowInfoHandle> info = android_view_InputWindowHandle_getHandle(env, inputWindow); auto ctrl = reinterpret_cast<SurfaceControl *>(nativeObject); - transaction->setInputWindowInfo(ctrl, *handle->getInfo()); + transaction->setInputWindowInfo(ctrl, std::move(info)); } static void nativeAddWindowInfosReportedListener(JNIEnv* env, jclass clazz, jlong transactionObj, diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto index 606e829c41fa..6af742fb23f4 100644 --- a/core/proto/android/providers/settings/secure.proto +++ b/core/proto/android/providers/settings/secure.proto @@ -104,6 +104,7 @@ message SecureSettingsProto { optional SettingProto accessibility_single_finger_panning_enabled = 56 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto accessibility_gesture_targets = 57 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto display_daltonizer_saturation_level = 58 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto accessibility_key_gesture_targets = 59 [ (android.privacy).dest = DEST_AUTOMATIC ]; } optional Accessibility accessibility = 2; diff --git a/core/res/Android.bp b/core/res/Android.bp index 66c2e12f7cdf..0e4e22b09e24 100644 --- a/core/res/Android.bp +++ b/core/res/Android.bp @@ -158,6 +158,7 @@ android_app { flags_packages: [ "android.app.appfunctions.flags-aconfig", "android.app.contextualsearch.flags-aconfig", + "android.app.flags-aconfig", "android.appwidget.flags-aconfig", "android.content.pm.flags-aconfig", "android.provider.flags-aconfig", @@ -171,7 +172,9 @@ android_app { "android.security.flags-aconfig", "com.android.hardware.input.input-aconfig", "aconfig_trade_in_mode_flags", + "art-aconfig-flags", "ranging_aconfig_flags", + "aconfig_settingslib_flags", ], } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index a6bf9151e0ab..3e0c1200749e 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -4463,6 +4463,18 @@ android:description="@string/permdesc_hideOverlayWindows" android:protectionLevel="normal" /> + <!-- Allows an app to enter Picture-in-Picture mode when the user is not explicitly requesting + it. This includes using {@link PictureInPictureParams.Builder#setAutoEnterEnabled} as well + as lifecycle methods such as {@link Activity#onUserLeaveHint} and {@link Activity#onPause} + to enter PiP when the user leaves the app. + This permission should only be used for certain PiP + <a href="{@docRoot}training/tv/get-started/multitasking#usage-types">usage types</a>. + @FlaggedApi(android.app.Flags.FLAG_ENABLE_TV_IMPLICIT_ENTER_PIP_RESTRICTION) + --> + <permission android:name="android.permission.TV_IMPLICIT_ENTER_PIP" + android:protectionLevel="normal" + android:featureFlag="android.app.enable_tv_implicit_enter_pip_restriction" /> + <!-- ================================== --> <!-- Permissions affecting the system wallpaper --> <!-- ================================== --> @@ -4969,6 +4981,27 @@ <permission android:name="android.permission.PROVIDE_REMOTE_CREDENTIALS" android:protectionLevel="signature|privileged|role" /> + <!-- @FlaggedApi(com.android.settingslib.flags.Flags.FLAG_SETTINGS_CATALYST) + Allows an application to access the Settings Preference services to read settings exposed + by the system Settings app and system apps that contribute settings surfaced by the + Settings app. + <p>This allows the calling application to read settings values through the host + application, agnostic of underlying storage. --> + <permission android:name="android.permission.READ_SYSTEM_PREFERENCES" + android:protectionLevel="signature|privileged|role" + android:featureFlag="com.android.settingslib.flags.settings_catalyst" /> + + <!-- @FlaggedApi(com.android.settingslib.flags.Flags.FLAG_SETTINGS_CATALYST) + Allows an application to access the Settings Preference services to write settings + values exposed by the system Settings app and system apps that contribute settings surfaced + in the Settings app. + <p>This allows the calling application to write settings values + through the host application, agnostic of underlying storage. + <p>Protection Level: signature|privileged|appop - appop to be added in followup --> + <permission android:name="android.permission.WRITE_SYSTEM_PREFERENCES" + android:protectionLevel="signature|privileged" + android:featureFlag="com.android.settingslib.flags.settings_catalyst" /> + <!-- ========================================= --> <!-- Permissions for special development tools --> <!-- ========================================= --> @@ -8302,6 +8335,26 @@ android:protectionLevel="signature|knownSigner" android:knownCerts="@array/config_healthConnectMigrationKnownSigners" /> + <!-- @hide @SystemApi Allows permitted apps to back up Health Connect data and settings. + <p>Protection level: signature|knownSigner + @FlaggedApi("android.permission.flags.health_connect_backup_restore_permission_enabled") + --> + <permission + android:name="android.permission.BACKUP_HEALTH_CONNECT_DATA_AND_SETTINGS" + android:protectionLevel="signature|knownSigner" + android:knownCerts="@array/config_backupHealthConnectDataAndSettingsKnownSigners" + android:featureFlag="android.permission.flags.health_connect_backup_restore_permission_enabled" /> + + <!-- @hide @SystemApi Allows permitted apps to restore Health Connect data and settings. + <p>Protection level: signature|knownSigner + @FlaggedApi("android.permission.flags.health_connect_backup_restore_permission_enabled") + --> + <permission + android:name="android.permission.RESTORE_HEALTH_CONNECT_DATA_AND_SETTINGS" + android:protectionLevel="signature|knownSigner" + android:knownCerts="@array/config_restoreHealthConnectDataAndSettingsKnownSigners" + android:featureFlag="android.permission.flags.health_connect_backup_restore_permission_enabled" /> + <!-- @SystemApi Allows an app to query apps in clone profile. The permission is bidirectional in nature, i.e. cloned apps would be able to query apps in root user. The permission is not meant for 3P apps as of now. @@ -8491,6 +8544,16 @@ android:protectionLevel="signature|privileged" android:featureFlag="com.android.tradeinmode.flags.enable_trade_in_mode" /> + <!-- @SystemApi + @FlaggedApi(com.android.art.flags.Flags.FLAG_EXECUTABLE_METHOD_FILE_OFFSETS) + Ability to read program metadata and attach dynamic instrumentation. + <p>Protection level: signature + @hide + --> + <permission android:name="android.permission.DYNAMIC_INSTRUMENTATION" + android:protectionLevel="signature" + android:featureFlag="com.android.art.flags.executable_method_file_offsets" /> + <!-- @TestApi Signature permission reserved for testing. This should never be used to diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml index 1e3f40296b43..9cfaca792f64 100644 --- a/core/res/res/values-af/strings.xml +++ b/core/res/res/values-af/strings.xml @@ -612,6 +612,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Laat die program toe om relatiewe posisie tussen ultrabreëbandtoestelle in die omtrek te bepaal"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"om interaksie met wi‑fi-toestelle in die omtrek te hê"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Laat die program toe om op toestelle in die omtrek te adverteer, aan hulle te koppel en hul relatiewe posisie te bepaal"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Voorkeur-NFC-betalingdiensinligting"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Laat die program toe om voorkeur-NFC-betalingdiensinligting soos geregistreerde hulpmiddels en roetebestemming te kry."</string> <string name="permlab_nfc" msgid="1904455246837674977">"beheer kortveldkommunikasie"</string> diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml index 91b4ef017014..c30a5c853421 100644 --- a/core/res/res/values-am/strings.xml +++ b/core/res/res/values-am/strings.xml @@ -612,6 +612,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"በአቅራቢያ ባሉ ልዕለ-ሰፊ ባንድ መሣሪያዎች መካከል ያለውን አንጻራዊ አቀማመጣቸውን ለማወቅ ንዲችል ለመተግበሪያው ይፍቀዱ"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"በአቅራቢያ ካሉ የWi‑Fi መሣሪያዎች ጋር መስተጋብር መፍጠር"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"መተግበሪያው በአቅራቢያ ያሉ የWi-Fi መሣሪያዎች አንጻራዊ ቦታን እንዲያሳውቅ፣ እንዲያገናኝ እና እንዲያውቅ ያስችለዋል"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"ተመራጭ NFC የክፍያ አገልግሎት መረጃ"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"እንደ የተመዘገቡ እርዳታዎች እና የጉዞ መሥመር መዳረሻ የመሳሰለ ተመራጭ nfc የክፍያ አገልግሎት መረጃን ለማግኘት ለመተግበሪያው ያፈቅድለታል።"</string> <string name="permlab_nfc" msgid="1904455246837674977">"ቅርብ የግኑኙነትመስክ (NFC) ተቆጣጠር"</string> diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml index 6e5dcd36fd31..f2080cbbe3d9 100644 --- a/core/res/res/values-ar/strings.xml +++ b/core/res/res/values-ar/strings.xml @@ -616,6 +616,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"يسمح هذا الإذن للتطبيق بتحديد الموضع النسبي بين الأجهزة المجاورة التي تستخدم النطاق الواسع جدًا."</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"التفاعل مع أجهزة Wi‑Fi المجاورة"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"للسماح للتطبيق بعرض الإعلانات والاتصال بالأجهزة الأخرى وتحديد الموقع النسبي لأجهزة Wi-Fi المجاورة."</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"معلومات الخدمات المدفوعة باستخدام الاتصال قصير المدى NFC المفضّل"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"يسمح هذا الإذن للتطبيق بالحصول على معلومات الخدمات المدفوعة باستخدام الاتصال قصير المدى NFC المفضّل، مثلاً المساعدات المسجّلة ووجهة المسار."</string> <string name="permlab_nfc" msgid="1904455246837674977">"التحكم في اتصال الحقل القريب"</string> @@ -2435,54 +2439,30 @@ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"تفعيل"</string> <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"رجوع"</string> <string name="unarchival_session_app_label" msgid="6811856981546348205">"بانتظار الإزالة من الأرشيف…"</string> - <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) --> - <skip /> - <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) --> - <skip /> + <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"تتوفّر الآن ميزة \"اتصالات طوارئ بالقمر الصناعي\""</string> + <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"يمكنك مراسلة خدمات الطوارئ في حال عدم توفّر شبكة جوّال أو شبكة Wi-Fi. ولإجراء ذلك، يجب ضبط تطبيق \"رسائل Google\" ليصبح تطبيق المراسلة التلقائي."</string> + <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"ميزة \"اتصالات طوارئ بالقمر الصناعي\" غير متاحة"</string> + <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"ميزة \"اتصالات طوارئ بالقمر الصناعي\" غير متاحة على هذا الجهاز"</string> + <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"لم يتم ضبط إعدادات ميزة \"اتصالات طوارئ بالقمر الصناعي\""</string> + <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"يُرجى التأكّد من أنّ جهازك متصل بالإنترنت ومحاولة ضبط الميزة مرة أخرى"</string> + <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\" ليصبح تطبيق المراسلة التلقائي"</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> + <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"يمكنك إرسال رسائل عبر القمر الصناعي في حال عدم توفّر شبكة جوّال أو شبكة Wi-Fi. ولإجراء ذلك، يجب ضبط تطبيق \"رسائل Google\" ليصبح تطبيق المراسلة التلقائي."</string> + <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"ميزة \"المراسلة عبر القمر الاصطناعي\" غير متاحة"</string> + <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"ميزة \"المراسلة عبر القمر الاصطناعي\" غير متاحة على هذا الجهاز"</string> + <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"لم يتم ضبط إعدادات ميزة \"المراسلة عبر القمر الاصطناعي\""</string> + <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"يُرجى التأكّد من أنّ جهازك متصل بالإنترنت ومحاولة ضبط الميزة مرة أخرى"</string> + <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\" ليصبح تطبيق المراسلة التلقائي"</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> <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-as/strings.xml b/core/res/res/values-as/strings.xml index 1af7810acfeb..1f207d571c5a 100644 --- a/core/res/res/values-as/strings.xml +++ b/core/res/res/values-as/strings.xml @@ -612,6 +612,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"এপ্টোক নিকটৱৰ্তী আল্ট্ৰা-ৱাইডবেণ্ড ডিভাইচসমূহৰ মাজৰ আপেক্ষিক স্থান নিৰ্ধাৰণ কৰিবলৈ অনুমতি দিয়ক"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"নিকটৱৰ্তী ৱাই-ফাই ডিভাইচসমূহৰ সৈতে ভাব বিনিময় কৰক"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"এপ্টোক বিজ্ঞাপন প্ৰচাৰাভিযান কৰিবলৈ, সংযোগ কৰিবলৈ আৰু নিকটৱৰ্তী ৱাই-ফাই ডিভাইচৰ আপেক্ষিক স্থান নিৰ্ধাৰণ কৰিবলৈ অনুমতি দিয়ে"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"অগ্ৰাধিকাৰ দিয়া NFC পৰিশোধ সেৱাৰ তথ্য"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"এপ্টোক অগ্ৰাধিকাৰ দিয়া nfc পৰিশোধ সেৱাৰ পঞ্জীকৃত সহায়কসমূহ আৰু পৰিশোধ কৰিব লগা লক্ষ্যস্থান দৰে তথ্য পাবলৈ অনুমতি দিয়ে।"</string> <string name="permlab_nfc" msgid="1904455246837674977">"নিয়েৰ ফিল্ড কমিউনিকেশ্বন নিয়ন্ত্ৰণ কৰক"</string> @@ -2431,54 +2435,30 @@ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"অন কৰক"</string> <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"উভতি যাওক"</string> <string name="unarchival_session_app_label" msgid="6811856981546348205">"বিবেচনাধীন হৈ আছে..."</string> - <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) --> - <skip /> - <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) --> - <skip /> + <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"উপগ্ৰহ SOS সুবিধাটো এতিয়া উপলব্ধ"</string> + <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"যদি ম’বাইল বা ৱাই-ফাই নেটৱৰ্ক নাথাকিলেও আপুনি জৰুৰীকালীন সেৱাসমূহলৈ বার্তা পঠিয়াব পাৰে। Google Messages আপোনাৰ ডিফ’ল্ট বাৰ্তা আদান-প্ৰদান কৰা এপ্ হ’বই লাগিব।"</string> + <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"উপগ্ৰহ SOS সুবিধা সমৰ্থিত নহয়"</string> + <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"এই ডিভাইচটোত উপগ্ৰহ SOS সুবিধা সমৰ্থিত নহয়"</string> + <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"উপগ্ৰহ SOS সুবিধা ছেট আপ কৰা হোৱা নাই"</string> + <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"আপুনি ইণ্টাৰনেটৰ সৈতে সংযুক্ত হৈ থকাটো নিশ্চিত কৰক আৰু পুনৰ ছেটআপ কৰিবলৈ চেষ্টা কৰক"</string> + <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"উপগ্ৰহ SOS সুবিধা উপলব্ধ নহয়"</string> + <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"এই দেশ বা অঞ্চলত উপগ্ৰহ SOS সুবিধা উপলব্ধ নহয়"</string> + <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"উপগ্ৰহ SOS সুবিধা ছেট আপ কৰা হোৱা নাই"</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">"উপগ্ৰহ SOS সুবিধা উপলব্ধ নহয়"</string> + <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"এই দেশ বা অঞ্চলত উপগ্ৰহ SOS সুবিধা উপলব্ধ হয় নে নহয় পৰীক্ষা কৰিবলৈ, অৱস্থানৰ ছেটিং অন কৰক"</string> + <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"উপগ্ৰহৰ দ্বাৰা বাৰ্তা বিনিময়ৰ সুবিধা উপলব্ধ"</string> + <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"ম’বাইল বা ৱাই-ফাই নেটৱৰ্ক নাথাকিলেও আপুনি উপগ্ৰহৰ জৰিয়তে বার্তা পঠিয়াব পাৰে। Google Messages আপোনাৰ ডিফ’ল্ট বাৰ্তা আদান-প্ৰদান কৰা এপ্ হ’বই লাগিব।"</string> + <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"উপগ্ৰহৰ দ্বাৰা বাৰ্তা বিনিময়ৰ সুবিধা সমৰ্থিত নহয়"</string> + <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"এই ডিভাইচটোত উপগ্ৰহৰ দ্বাৰা বাৰ্তা বিনিময়ৰ সুবিধা সমৰ্থিত নহয়"</string> + <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"উপগ্ৰহৰ দ্বাৰা বাৰ্তা বিনিময়ৰ সুবিধা ছেট আপ কৰা হোৱা নাই"</string> + <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"আপুনি ইণ্টাৰনেটৰ সৈতে সংযুক্ত হৈ থকাটো নিশ্চিত কৰক আৰু পুনৰ ছেটআপ কৰিবলৈ চেষ্টা কৰক"</string> + <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_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> <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-az/strings.xml b/core/res/res/values-az/strings.xml index 1ba96fea7a19..07216f55bdcd 100644 --- a/core/res/res/values-az/strings.xml +++ b/core/res/res/values-az/strings.xml @@ -612,6 +612,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Tətbiqə yaxınlıqdakı Ultra Genişzolaqlı cihazları arasında nisbi mövqeyi təyin etməyə icazə verin"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"yaxınlıqdakı Wi-Fi cihazları ilə əlaqə qurmaq"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Tətbiqə yaxınlıqdakı Wi-Fi cihazlarında reklam etmək, onlara qoşulmaq və nisbi mövqeyini təyin etmək icazəsi verir"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Tərcih edilən NFC ödəniş xidməti məlumatı"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Tətbiqə qeydiyyatdan keçmiş yardım və marşrut təyinatı kimi tərcih edilən nfc ödəniş xidməti məlumatını əldə etmək icazəsi verir."</string> <string name="permlab_nfc" msgid="1904455246837674977">"Near Field Communication\'ı kontrol et"</string> diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml index 4c78d30c874e..ac6a47fd8f84 100644 --- a/core/res/res/values-b+sr+Latn/strings.xml +++ b/core/res/res/values-b+sr+Latn/strings.xml @@ -613,6 +613,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Dozvoljava aplikaciji da određuje relativnu razdaljinu između uređaja ultra-širokog pojasa u blizini"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"interakcija sa WiFi uređajima u blizini"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Dozvoljava aplikaciji da se oglašava, povezuje i utvrđuje relativnu poziciju WiFi uređaja u blizini"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informacije o željenoj NFC usluzi za plaćanje"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Dozvoljava aplikaciji da preuzima informacije o željenoj NFC usluzi za plaćanje, poput registrovanih identifikatora aplikacija i odredišta preusmeravanja."</string> <string name="permlab_nfc" msgid="1904455246837674977">"kontrola komunikacije u užem polju (Near Field Communication)"</string> @@ -2432,54 +2436,30 @@ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Uključi"</string> <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Nazad"</string> <string name="unarchival_session_app_label" msgid="6811856981546348205">"Na čekanju..."</string> - <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) --> - <skip /> - <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) --> - <skip /> + <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"Hitna pomoć preko satelita je sada dostupna"</string> + <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"Možete da šaljete poruke hitnim službama ako nemate pristup mobilnoj ni WiFi mreži. Google Messages mora da bude podrazumevana aplikacija za razmenu poruka."</string> + <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"Hitna pomoć preko satelita nije podržana"</string> + <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"Hitna pomoć preko satelita nije podržana na ovom uređaju"</string> + <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"Hitna pomoć preko satelita nije podešena"</string> + <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"Proverite da li ste povezani na internet i probajte ponovo da podesite"</string> + <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"Hitna pomoć preko satelita nije dostupna"</string> + <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"Hitna pomoć preko satelita nije dostupna u ovoj zemlji ili regionu"</string> + <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"Hitna pomoć preko satelita nije podešena"</string> + <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"Da biste slali poruke preko satelita, podesite Google Messages kao podrazumevanu aplikaciju za razmenu poruka"</string> + <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"Hitna pomoć preko satelita nije dostupna"</string> + <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"Da biste proverili da li je hitna pomoć preko satelita dostupna u ovoj zemlji ili regionu, uključite podešavanja lokacije"</string> + <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"Razmena poruka preko satelita je dostupna"</string> + <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"Možete da šaljete poruke preko satelita ako nemate pristup mobilnoj ni WiFi mreži. Google Messages mora da bude podrazumevana aplikacija za razmenu poruka."</string> + <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"Razmena poruka preko satelita nije podržana"</string> + <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"Razmena poruka preko satelita nije podržana na ovom uređaju"</string> + <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"Razmena poruka preko satelita nije podešena"</string> + <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"Proverite da li ste povezani na internet i probajte ponovo da podesite"</string> + <string name="satellite_messaging_not_in_allowed_region_notification_title" msgid="2035303593479031655">"Razmena poruka preko satelita nije dostupna"</string> + <string name="satellite_messaging_not_in_allowed_region_notification_summary" msgid="5270294879531815854">"Razmena poruka preko satelita nije dostupna u ovoj zemlji ili regionu"</string> + <string name="satellite_messaging_unsupported_default_sms_app_notification_title" msgid="1004808759472360189">"Razmena poruka preko satelita nije podešena"</string> + <string name="satellite_messaging_unsupported_default_sms_app_notification_summary" msgid="17084124893763593">"Da biste slali poruke preko satelita, podesite Google Messages kao podrazumevanu aplikaciju za razmenu poruka"</string> + <string name="satellite_messaging_location_disabled_notification_title" msgid="7270641894250928494">"Razmena poruka preko satelita nije dostupna"</string> + <string name="satellite_messaging_location_disabled_notification_summary" msgid="1450824950686221810">"Da biste proverili da li je razmena poruka preko satelita dostupna u ovoj zemlji ili regionu, uključite podešavanja lokacije"</string> <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Ponovo podesite otključavanje otiskom prsta"</string> <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> više ne može da se prepozna."</string> <string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> i <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> više ne mogu da se prepoznaju."</string> diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml index 549ec78cc7d8..f492bfa4cc7b 100644 --- a/core/res/res/values-be/strings.xml +++ b/core/res/res/values-be/strings.xml @@ -614,6 +614,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Дазволіць праграме вызначаць адлегласць паміж прыладамі паблізу, якія выкарыстоўваюць звышшырокапалосную сувязь"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"узаемадзейнічаць з прыладамі з Wi‑Fi паблізу"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Праграма зможа адпраўляць даныя на прылады Wi-Fi паблізу, падключацца да іх і вызначаць іх месцазнаходжанне"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Інфармацыя пра прыярытэтны сэрвіс аплаты NFC"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Дазваляе праграме атрымаць доступ да інфармацыі пра прыярытэтны сэрвіс аплаты NFC, напрыклад зарэгістраваныя ідэнтыфікатары праграм і маршруты адпраўкі даных."</string> <string name="permlab_nfc" msgid="1904455246837674977">"кантроль Near Field Communication"</string> @@ -2433,54 +2437,30 @@ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Уключыць"</string> <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Назад"</string> <string name="unarchival_session_app_label" msgid="6811856981546348205">"У чаканні..."</string> - <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) --> - <skip /> - <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) --> - <skip /> + <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"Экстраннае спадарожнікавае падключэнне зараз даступнае"</string> + <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"У вас ёсць магчымасць адпраўляць паведамленні ў экстранныя службы, калі адсутнічае падключэнне да мабільнай сеткі ці сеткі Wi-Fi. Упэўніцеся, што ў якасці стандартнай выбрана праграма \"Google Паведамленні\"."</string> + <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"Экстраннае спадарожнікавае падключэнне не падтрымліваецца"</string> + <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"Экстраннае спадарожнікавае падключэнне не падтрымліваецца на гэтай прыладзе"</string> + <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"Экстраннае спадарожнікавае падключэнне не наладжана"</string> + <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"Праверце падключэнне да інтэрнэту і паўтарыце наладжванне"</string> + <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 Паведамленні\" павінна быць выбрана як стандартная праграма абмену паведамленнямі"</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> + <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"Вы можаце абменьвацца паведамленнямі па спадарожнікавай сувязі, калі падключэнне да мабільнай сеткі ці сеткі Wi-Fi адсутнічае. Упэўніцеся, што ў якасці стандартнай выбрана праграма \"Google Паведамленні\"."</string> + <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"Абмен паведамленнямі па спадарожнікавай сувязі не падтрымліваецца"</string> + <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"Абмен паведамленнямі па спадарожнікавай сувязі не падтрымліваецца на гэтай прыладзе"</string> + <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"Абмен паведамленнямі па спадарожнікавай сувязі не наладжаны"</string> + <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"Праверце падключэнне да інтэрнэту і паўтарыце наладжванне"</string> + <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 Паведамленні\" павінна быць выбрана як стандартная праграма абмену паведамленнямі"</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> <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-bg/strings.xml b/core/res/res/values-bg/strings.xml index 40aae5144448..c8b171af5bc9 100644 --- a/core/res/res/values-bg/strings.xml +++ b/core/res/res/values-bg/strings.xml @@ -612,6 +612,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Разрешаване на приложението да определя относителната позиция между устройствата с ултрашироколентови сигнали в близост"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"взаимодействие с устройствата с Wi-Fi в близост"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Разрешава на приложението да рекламира, да се свързва и да определя относителната позиция на устройствата с Wi-Fi в близост"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Информация за предпочитаната услуга за плащане чрез NFC"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Дава възможност на приложението да получава информация за предпочитаната услуга за плащане чрез NFC, като например регистрирани помощни средства и местоназначение."</string> <string name="permlab_nfc" msgid="1904455246837674977">"контролиране на комуникацията в близкото поле"</string> diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml index 32077d8a4374..0e103cc424b0 100644 --- a/core/res/res/values-bn/strings.xml +++ b/core/res/res/values-bn/strings.xml @@ -612,6 +612,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"অ্যাপকে আশেপাশের Ultra-Wideband ডিভাইসগুলির আপেক্ষিক অবস্থান নির্ণয় করার অনুমতি দিন"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"আশপাশের ওয়াই-ফাই ডিভাইসের সাথে ইন্টার্যাক্ট করুন"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"এটির ফলে অ্যাপ আশপাশের ওয়াই-ফাই ডিভাইসের তথ্য দেখতে, তাদের সাথে কানেক্ট করতে এবং তা কত দূরত্বে আছে সেটি জানতে পারবে"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"পছন্দের NFC পেমেন্ট পরিষেবার তথ্য"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"অ্যাপের মাধ্যমে পছন্দসই এনএফসি পেমেন্ট পরিষেবার তথ্য, যেমন রেজিস্ট্রার করার সহায়তা এবং রুট ডেস্টিনেশন সম্পর্কিত তথ্য অ্যাক্সেস করার অনুমতি দেয়।"</string> <string name="permlab_nfc" msgid="1904455246837674977">"নিয়ার ফিল্ড কমিউনিকেশন নিয়ন্ত্রণ করে"</string> @@ -2431,54 +2435,30 @@ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"চালু করুন"</string> <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"ফিরে যান"</string> <string name="unarchival_session_app_label" msgid="6811856981546348205">"বাকি আছে…"</string> - <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) --> - <skip /> - <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) --> - <skip /> + <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"স্যাটেলাইট SOS এখন উপলভ্য"</string> + <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"কোনও মোবাইল বা ওয়াই-ফাই নেটওয়ার্ক না থাকলেও আপনি জরুরি পরিষেবাতে মেসেজ করতে পারবেন। Google Messages অবশ্যই আপনার ডিফল্ট মেসেজিং অ্যাপ হতে হবে।"</string> + <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"স্যাটেলাইট SOS কাজ করে না"</string> + <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"এই ডিভাইসে স্যাটেলাইট SOS কাজ করে না"</string> + <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"স্যাটেলাইট SOS সেট আপ করা হয়নি"</string> + <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"আপনার ডিভাইসে ইন্টারনেট কানেকশন আছে কিনা দেখে নিন এবং আবার সেটআপ করার চেষ্টা করুন"</string> + <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"স্যাটেলাইট SOS উপলভ্য নেই"</string> + <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"এই দেশ অথবা অঞ্চলে স্যাটেলাইট SOS উপলভ্য নেই"</string> + <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"স্যাটেলাইট SOS সেট আপ করা নেই"</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">"স্যাটেলাইট SOS উপলভ্য নেই"</string> + <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"এই দেশে বা অঞ্চলে স্যাটেলাইট SOS উপলভ্য আছে কিনা দেখতে, লোকেশন সেটিংস চালু করুন"</string> + <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"\'স্যাটেলাইট মেসেজিং\' ফিচার উপলভ্য"</string> + <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"কোনও মোবাইল বা ওয়াই-ফাই নেটওয়ার্ক না থাকলে, স্যাটেলাইটের মাধ্যমে মেসেজ করতে পারবেন। Google Messages অবশ্যই আপনার ডিফল্ট মেসেজিং অ্যাপ হতে হবে।"</string> + <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"\'স্যাটেলাইট মেসেজিং\' ফিচার কাজ করে না"</string> + <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"এই ডিভাইসে \'স্যাটেলাইট মেসেজিং\' ফিচার কাজ করে না"</string> + <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"\'স্যাটেলাইট মেসেজিং\' ফিচার সেট আপ করা নেই"</string> + <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"আপনার ডিভাইসে ইন্টারনেট কানেকশন আছে কিনা দেখে নিন এবং আবার সেটআপ করার চেষ্টা করুন"</string> + <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_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> <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-bs/strings.xml b/core/res/res/values-bs/strings.xml index 266dee2b984a..202afdeb9438 100644 --- a/core/res/res/values-bs/strings.xml +++ b/core/res/res/values-bs/strings.xml @@ -613,6 +613,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Dozvolite aplikaciji da odredi relativni položaj između uređaja ultra širokog opsega u blizini"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"stupanje u interakciju s WiFi uređajima u blizini"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Dozvoljava aplikaciji da se oglašava, povezuje i određuje relativni položaj WiFi uređaja u blizini"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informacije o preferiranoj usluzi plaćanja putem NFC-a"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Dozvoljava aplikaciji da dobije informacije o preferiranoj usluzi plaćanja putem NFC-a kao što su registrirana pomagala i odredište rute."</string> <string name="permlab_nfc" msgid="1904455246837674977">"upravljanje NFC-om"</string> @@ -2432,54 +2436,30 @@ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Uključi"</string> <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Nazad"</string> <string name="unarchival_session_app_label" msgid="6811856981546348205">"Na čekanju…"</string> - <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) --> - <skip /> - <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) --> - <skip /> + <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"Hitna pomoć putem satelita je sada dostupna"</string> + <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"Možete razmjenjivati poruke s hitnim službama ako nemate mobilnu ili WiFi mrežu. Google Messages mora biti zadana aplikacija za razmjenu poruka."</string> + <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"Hitna pomoć putem satelita nije podržana"</string> + <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"Hitna pomoć putem satelita nije podržana na uređaju"</string> + <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"Hitna pomoć putem satelita nije postavljena"</string> + <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"Provjerite jeste li povezani s internetom i ponovo pokušajte postaviti uslugu"</string> + <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"Hitna pomoć putem satelita nije dostupna"</string> + <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"Hitna pomoć putem satelita trenutno nije dostupna u ovoj zemlji ili regiji"</string> + <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"Hitna pomoć putem satelita nije postavljena"</string> + <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"Da razmjenjujete poruke putem satelita, postavite Google Messages kao zadanu aplikaciju za razmjenu poruka"</string> + <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"Hitna pomoć putem satelita nije dostupna"</string> + <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"Da provjerite je li hitna pomoć putem satelita dostupna u vašoj zemlji ili regiji, uključite postavke lokacije"</string> + <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"Satelitska razmjena poruka je dostupna"</string> + <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"Možete razmjenjivati poruke putem satelita ako nemate mobilnu ili WiFi mrežu. Google Messages mora biti zadana aplikacija za razmjenu poruka."</string> + <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"Satelitska razmjena poruka nije podržana"</string> + <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"Satelitska razmjena poruka nije podržana na uređaju"</string> + <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"Satelitska razmjena poruka nije postavljena"</string> + <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"Provjerite jeste li povezani s internetom i ponovo pokušajte postaviti uslugu"</string> + <string name="satellite_messaging_not_in_allowed_region_notification_title" msgid="2035303593479031655">"Satelitska razmjena poruka nije dostupna"</string> + <string name="satellite_messaging_not_in_allowed_region_notification_summary" msgid="5270294879531815854">"Satelitska razmjena poruka nije dostupna u ovoj zemlji ili regiji"</string> + <string name="satellite_messaging_unsupported_default_sms_app_notification_title" msgid="1004808759472360189">"Satelitska razmjena poruka nije postavljena"</string> + <string name="satellite_messaging_unsupported_default_sms_app_notification_summary" msgid="17084124893763593">"Da razmjenjujete poruke putem satelita, postavite Google Messages kao zadanu aplikaciju za razmjenu poruka"</string> + <string name="satellite_messaging_location_disabled_notification_title" msgid="7270641894250928494">"Satelitska razmjena poruka nije dostupna"</string> + <string name="satellite_messaging_location_disabled_notification_summary" msgid="1450824950686221810">"Da provjerite je li satelitska razmjena poruka dostupna u vašoj zemlji ili regiji, uključite postavke lokacije"</string> <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Ponovo postavite otključavanje otiskom prsta"</string> <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> se više ne može prepoznati."</string> <string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> i <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> se više ne mogu prepoznati."</string> @@ -2489,7 +2469,7 @@ <string name="face_dangling_notification_msg" msgid="746235263598985384">"Vaš model lica se više ne može prepoznati. Ponovo postavite otključavanje licem."</string> <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Postavite"</string> <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Ne sada"</string> - <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"Alarm za: <xliff:g id="USER_NAME">%s</xliff:g>"</string> + <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"Alarm za korisnika <xliff:g id="USER_NAME">%s</xliff:g>"</string> <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"Prebaci na drugog korisnika"</string> <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"Isključi zvuk"</string> <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"Dodirnite da isključite zvuk"</string> diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml index 691126bb58ba..8b28cd7891c8 100644 --- a/core/res/res/values-ca/strings.xml +++ b/core/res/res/values-ca/strings.xml @@ -613,6 +613,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Permet que l\'aplicació determini la posició relativa entre els dispositius de banda ultraampla propers"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"interaccionar amb els dispositius Wi‑Fi propers"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Permet que l\'aplicació s\'anunciï i es connecti als dispositius Wi‑Fi propers, i en determini la posició relativa"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informació preferent sobre el servei de pagament per NFC"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Permet que l\'aplicació obtingui informació preferent sobre el servei de pagament per NFC, com ara complements registrats i destinacions de rutes."</string> <string name="permlab_nfc" msgid="1904455246837674977">"controlar Comunicació de camp proper (NFC)"</string> diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml index f6ee489802a9..f3a020bbfce4 100644 --- a/core/res/res/values-cs/strings.xml +++ b/core/res/res/values-cs/strings.xml @@ -614,6 +614,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Aplikace bude moci zjišťovat vzájemnou pozici mezi ultra-širokopásmovými zařízeními v okolí"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"interakce se zařízeními Wi-Fi v okolí"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Umožňuje aplikaci inzerovat, připojovat se a odhadovat relativní polohu zařízení Wi-Fi v okolí"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informace o preferované platební službě NFC"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Umožňuje aplikaci získat informace o preferované platební službě NFC, například o registrovaných pomůckách a cíli směrování."</string> <string name="permlab_nfc" msgid="1904455246837674977">"ovládání technologie NFC"</string> diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml index a861e3acfb23..b481682b0448 100644 --- a/core/res/res/values-da/strings.xml +++ b/core/res/res/values-da/strings.xml @@ -612,6 +612,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Tillad, at appen fastlægger den relative position mellem UWB-enheder (Ultra-Wideband) i nærheden"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"interagere med Wi‑Fi-enheder i nærheden"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Giver appen tilladelse til at informere om, oprette forbindelse til og fastslå den relative placering af Wi‑Fi-enheder i nærheden"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Foretrukne oplysninger vedrørende NFC-betalingstjeneste"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Tillader, at appen får foretrukne oplysninger vedrørende NFC-betalingstjeneste, f.eks. registrerede hjælpemidler og rutedestinationer."</string> <string name="permlab_nfc" msgid="1904455246837674977">"administrere Near Field Communication"</string> diff --git a/core/res/res/values-de-feminine/strings.xml b/core/res/res/values-de-feminine/strings.xml new file mode 100644 index 000000000000..ef7f3bb8d193 --- /dev/null +++ b/core/res/res/values-de-feminine/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/* //device/apps/common/assets/res/any/strings.xml +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT 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="owner_name" msgid="8713560351570795743">"Eigentümerin"</string> +</resources> diff --git a/core/res/res/values-de-masculine/strings.xml b/core/res/res/values-de-masculine/strings.xml new file mode 100644 index 000000000000..f8c46e7fb174 --- /dev/null +++ b/core/res/res/values-de-masculine/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/* //device/apps/common/assets/res/any/strings.xml +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT 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="owner_name" msgid="8713560351570795743">"Eigentümer"</string> +</resources> diff --git a/core/res/res/values-de-neuter/strings.xml b/core/res/res/values-de-neuter/strings.xml new file mode 100644 index 000000000000..f8c46e7fb174 --- /dev/null +++ b/core/res/res/values-de-neuter/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/* //device/apps/common/assets/res/any/strings.xml +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT 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="owner_name" msgid="8713560351570795743">"Eigentümer"</string> +</resources> diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml index 58c4c5e93ef1..64afb5adffc4 100644 --- a/core/res/res/values-de/strings.xml +++ b/core/res/res/values-de/strings.xml @@ -612,6 +612,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Ermöglicht der App, die relative Distanz zwischen Ultrabreitband-Geräten in der Nähe zu bestimmen"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"Mit WLAN-Geräten in der Nähe interagieren"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Erlaubt der App, Inhalte an WLAN-Geräte in der Nähe zu senden, sich mit ihnen zu verbinden und ihre relative Position zu ermitteln"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informationen zum bevorzugten NFC-Zahlungsdienst"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Ermöglicht der App, Informationen zum bevorzugten NFC-Zahlungsdienst abzurufen, etwa registrierte Hilfsmittel oder das Routenziel."</string> <string name="permlab_nfc" msgid="1904455246837674977">"Nahfeldkommunikation steuern"</string> diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml index 3a32c528d72f..4ad85d97201a 100644 --- a/core/res/res/values-el/strings.xml +++ b/core/res/res/values-el/strings.xml @@ -612,6 +612,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Επιτρέψτε στην εφαρμογή να προσδιορίζει τη σχετική θέση μεταξύ κοντινών συσκευών Ultra-Wideband"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"αλληλεπίδραση με κοντινές συσκευές Wi‑Fi"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Επιτρέπει στην εφαρμογή: προβολή διαφημίσεων, σύνδεση και καθορισμό της σχετικής τοποθεσίας των κοντινών συσκευών Wi‑Fi"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Πληροφορίες προτιμώμενης υπηρεσίας πληρωμών NFC"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Επιτρέπει στην εφαρμογή να λαμβάνει πληροφορίες προτιμώμενης υπηρεσίας πληρωμής NFC, όπως καταχωρημένα βοηθήματα και προορισμό διαδρομής."</string> <string name="permlab_nfc" msgid="1904455246837674977">"ελέγχει την Επικοινωνία κοντινού πεδίου (FNC)"</string> diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml index 84cc4095457d..a82b567a087e 100644 --- a/core/res/res/values-en-rAU/strings.xml +++ b/core/res/res/values-en-rAU/strings.xml @@ -612,6 +612,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Allow the app to determine relative position between nearby ultra-wideband devices"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"interact with nearby Wi‑Fi devices"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Allows the app to advertise, connect and determine the relative position of nearby Wi‑Fi devices"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Preferred NFC payment service information"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Allows the app to get preferred NFC payment service information, such as registered aids and route destination."</string> <string name="permlab_nfc" msgid="1904455246837674977">"control Near-Field Communication"</string> @@ -2431,54 +2435,30 @@ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Turn on"</string> <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Go back"</string> <string name="unarchival_session_app_label" msgid="6811856981546348205">"Pending…"</string> - <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) --> - <skip /> - <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) --> - <skip /> + <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"Satellite SOS is now available"</string> + <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"You can message emergency services if there\'s no mobile or Wi-Fi network. Google Messages must be your default messaging app."</string> + <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"Satellite SOS isn\'t supported"</string> + <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"Satellite SOS isn\'t supported on this device"</string> + <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"Satellite SOS isn\'t set up"</string> + <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"Make sure that you\'re connected to the Internet and try setup again"</string> + <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"Satellite SOS isn\'t available"</string> + <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"Satellite SOS isn\'t available in this country or region"</string> + <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"Satellite SOS not set up"</string> + <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"To message by satellite, set Google Messages as your default messaging app"</string> + <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"Satellite SOS isn\'t available"</string> + <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"To check if satellite SOS is available in this country or region, turn on location settings"</string> + <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"Satellite messaging available"</string> + <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"You can message by satellite if there\'s no mobile or Wi-Fi network. Google Messages must be your default messaging app."</string> + <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"Satellite messaging not supported"</string> + <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"Satellite messaging isn\'t supported on this device"</string> + <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"Satellite messaging not set up"</string> + <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"Make sure that you\'re connected to the Internet and try setup again"</string> + <string name="satellite_messaging_not_in_allowed_region_notification_title" msgid="2035303593479031655">"Satellite messaging not available"</string> + <string name="satellite_messaging_not_in_allowed_region_notification_summary" msgid="5270294879531815854">"Satellite messaging isn\'t available in this country or region"</string> + <string name="satellite_messaging_unsupported_default_sms_app_notification_title" msgid="1004808759472360189">"Satellite messaging not set up"</string> + <string name="satellite_messaging_unsupported_default_sms_app_notification_summary" msgid="17084124893763593">"To message by satellite, set Google Messages as your default messaging app"</string> + <string name="satellite_messaging_location_disabled_notification_title" msgid="7270641894250928494">"Satellite messaging not available"</string> + <string name="satellite_messaging_location_disabled_notification_summary" msgid="1450824950686221810">"To check if satellite messaging is available in this country or region, turn on location settings"</string> <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Set up Fingerprint Unlock again"</string> <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> can no longer be recognised."</string> <string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> and <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> can no longer be recognised."</string> @@ -2488,7 +2468,7 @@ <string name="face_dangling_notification_msg" msgid="746235263598985384">"Your face model can no longer be recognised. Set up Face Unlock again."</string> <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Set up"</string> <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Not now"</string> - <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"Alarm for: <xliff:g id="USER_NAME">%s</xliff:g>"</string> + <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"Alarm for <xliff:g id="USER_NAME">%s</xliff:g>"</string> <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"Switch user"</string> <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"Mute"</string> <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"Tap to mute sound"</string> diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml index 42250049636a..b09a79a63039 100644 --- a/core/res/res/values-en-rCA/strings.xml +++ b/core/res/res/values-en-rCA/strings.xml @@ -612,6 +612,8 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Allow the app to determine relative position between nearby Ultra-Wideband devices"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"interact with nearby Wi‑Fi devices"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Allows the app to advertise, connect, and determine the relative position of nearby Wi‑Fi devices"</string> + <string name="permlab_ranging" msgid="2854543350668593390">"determine relative position between nearby devices"</string> + <string name="permdesc_ranging" msgid="6703905535621521710">"Allow the app to determine relative position between nearby devices"</string> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Preferred NFC Payment Service Information"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Allows the app to get preferred nfc payment service information like registered aids and route destination."</string> <string name="permlab_nfc" msgid="1904455246837674977">"control Near Field Communication"</string> diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml index a80d0674d265..bf3b985ae6e8 100644 --- a/core/res/res/values-en-rGB/strings.xml +++ b/core/res/res/values-en-rGB/strings.xml @@ -612,6 +612,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Allow the app to determine relative position between nearby ultra-wideband devices"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"interact with nearby Wi‑Fi devices"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Allows the app to advertise, connect and determine the relative position of nearby Wi‑Fi devices"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Preferred NFC payment service information"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Allows the app to get preferred NFC payment service information, such as registered aids and route destination."</string> <string name="permlab_nfc" msgid="1904455246837674977">"control Near-Field Communication"</string> @@ -2431,54 +2435,30 @@ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Turn on"</string> <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Go back"</string> <string name="unarchival_session_app_label" msgid="6811856981546348205">"Pending…"</string> - <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) --> - <skip /> - <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) --> - <skip /> + <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"Satellite SOS is now available"</string> + <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"You can message emergency services if there\'s no mobile or Wi-Fi network. Google Messages must be your default messaging app."</string> + <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"Satellite SOS isn\'t supported"</string> + <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"Satellite SOS isn\'t supported on this device"</string> + <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"Satellite SOS isn\'t set up"</string> + <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"Make sure that you\'re connected to the Internet and try setup again"</string> + <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"Satellite SOS isn\'t available"</string> + <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"Satellite SOS isn\'t available in this country or region"</string> + <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"Satellite SOS not set up"</string> + <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"To message by satellite, set Google Messages as your default messaging app"</string> + <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"Satellite SOS isn\'t available"</string> + <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"To check if satellite SOS is available in this country or region, turn on location settings"</string> + <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"Satellite messaging available"</string> + <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"You can message by satellite if there\'s no mobile or Wi-Fi network. Google Messages must be your default messaging app."</string> + <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"Satellite messaging not supported"</string> + <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"Satellite messaging isn\'t supported on this device"</string> + <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"Satellite messaging not set up"</string> + <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"Make sure that you\'re connected to the Internet and try setup again"</string> + <string name="satellite_messaging_not_in_allowed_region_notification_title" msgid="2035303593479031655">"Satellite messaging not available"</string> + <string name="satellite_messaging_not_in_allowed_region_notification_summary" msgid="5270294879531815854">"Satellite messaging isn\'t available in this country or region"</string> + <string name="satellite_messaging_unsupported_default_sms_app_notification_title" msgid="1004808759472360189">"Satellite messaging not set up"</string> + <string name="satellite_messaging_unsupported_default_sms_app_notification_summary" msgid="17084124893763593">"To message by satellite, set Google Messages as your default messaging app"</string> + <string name="satellite_messaging_location_disabled_notification_title" msgid="7270641894250928494">"Satellite messaging not available"</string> + <string name="satellite_messaging_location_disabled_notification_summary" msgid="1450824950686221810">"To check if satellite messaging is available in this country or region, turn on location settings"</string> <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Set up Fingerprint Unlock again"</string> <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> can no longer be recognised."</string> <string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> and <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> can no longer be recognised."</string> @@ -2488,7 +2468,7 @@ <string name="face_dangling_notification_msg" msgid="746235263598985384">"Your face model can no longer be recognised. Set up Face Unlock again."</string> <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Set up"</string> <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Not now"</string> - <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"Alarm for: <xliff:g id="USER_NAME">%s</xliff:g>"</string> + <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"Alarm for <xliff:g id="USER_NAME">%s</xliff:g>"</string> <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"Switch user"</string> <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"Mute"</string> <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"Tap to mute sound"</string> diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml index c123499b6883..5c9c52148750 100644 --- a/core/res/res/values-en-rIN/strings.xml +++ b/core/res/res/values-en-rIN/strings.xml @@ -612,6 +612,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Allow the app to determine relative position between nearby ultra-wideband devices"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"interact with nearby Wi‑Fi devices"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Allows the app to advertise, connect and determine the relative position of nearby Wi‑Fi devices"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Preferred NFC payment service information"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Allows the app to get preferred NFC payment service information, such as registered aids and route destination."</string> <string name="permlab_nfc" msgid="1904455246837674977">"control Near-Field Communication"</string> @@ -2431,54 +2435,30 @@ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Turn on"</string> <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Go back"</string> <string name="unarchival_session_app_label" msgid="6811856981546348205">"Pending…"</string> - <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) --> - <skip /> - <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) --> - <skip /> + <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"Satellite SOS is now available"</string> + <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"You can message emergency services if there\'s no mobile or Wi-Fi network. Google Messages must be your default messaging app."</string> + <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"Satellite SOS isn\'t supported"</string> + <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"Satellite SOS isn\'t supported on this device"</string> + <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"Satellite SOS isn\'t set up"</string> + <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"Make sure that you\'re connected to the Internet and try setup again"</string> + <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"Satellite SOS isn\'t available"</string> + <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"Satellite SOS isn\'t available in this country or region"</string> + <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"Satellite SOS not set up"</string> + <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"To message by satellite, set Google Messages as your default messaging app"</string> + <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"Satellite SOS isn\'t available"</string> + <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"To check if satellite SOS is available in this country or region, turn on location settings"</string> + <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"Satellite messaging available"</string> + <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"You can message by satellite if there\'s no mobile or Wi-Fi network. Google Messages must be your default messaging app."</string> + <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"Satellite messaging not supported"</string> + <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"Satellite messaging isn\'t supported on this device"</string> + <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"Satellite messaging not set up"</string> + <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"Make sure that you\'re connected to the Internet and try setup again"</string> + <string name="satellite_messaging_not_in_allowed_region_notification_title" msgid="2035303593479031655">"Satellite messaging not available"</string> + <string name="satellite_messaging_not_in_allowed_region_notification_summary" msgid="5270294879531815854">"Satellite messaging isn\'t available in this country or region"</string> + <string name="satellite_messaging_unsupported_default_sms_app_notification_title" msgid="1004808759472360189">"Satellite messaging not set up"</string> + <string name="satellite_messaging_unsupported_default_sms_app_notification_summary" msgid="17084124893763593">"To message by satellite, set Google Messages as your default messaging app"</string> + <string name="satellite_messaging_location_disabled_notification_title" msgid="7270641894250928494">"Satellite messaging not available"</string> + <string name="satellite_messaging_location_disabled_notification_summary" msgid="1450824950686221810">"To check if satellite messaging is available in this country or region, turn on location settings"</string> <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Set up Fingerprint Unlock again"</string> <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> can no longer be recognised."</string> <string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> and <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> can no longer be recognised."</string> @@ -2488,7 +2468,7 @@ <string name="face_dangling_notification_msg" msgid="746235263598985384">"Your face model can no longer be recognised. Set up Face Unlock again."</string> <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Set up"</string> <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Not now"</string> - <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"Alarm for: <xliff:g id="USER_NAME">%s</xliff:g>"</string> + <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"Alarm for <xliff:g id="USER_NAME">%s</xliff:g>"</string> <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"Switch user"</string> <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"Mute"</string> <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"Tap to mute sound"</string> diff --git a/core/res/res/values-es-rUS-feminine/strings.xml b/core/res/res/values-es-rUS-feminine/strings.xml new file mode 100644 index 000000000000..bf181d022f9a --- /dev/null +++ b/core/res/res/values-es-rUS-feminine/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/* //device/apps/common/assets/res/any/strings.xml +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT 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="owner_name" msgid="8713560351570795743">"Propietaria"</string> +</resources> diff --git a/core/res/res/values-es-rUS-masculine/strings.xml b/core/res/res/values-es-rUS-masculine/strings.xml new file mode 100644 index 000000000000..4b67970f668b --- /dev/null +++ b/core/res/res/values-es-rUS-masculine/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/* //device/apps/common/assets/res/any/strings.xml +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT 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="owner_name" msgid="8713560351570795743">"Propietario"</string> +</resources> diff --git a/core/res/res/values-es-rUS-neuter/strings.xml b/core/res/res/values-es-rUS-neuter/strings.xml new file mode 100644 index 000000000000..4b67970f668b --- /dev/null +++ b/core/res/res/values-es-rUS-neuter/strings.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/* //device/apps/common/assets/res/any/strings.xml +** +** Copyright 2006, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT 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="owner_name" msgid="8713560351570795743">"Propietario"</string> +</resources> diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml index 26b4dfa4f2f4..ab6f6ce07c1d 100644 --- a/core/res/res/values-es-rUS/strings.xml +++ b/core/res/res/values-es-rUS/strings.xml @@ -613,6 +613,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Permite que la app determine la posición relativa con dispositivos Ultra Wideband cercanos"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"interactuar con dispositivos Wi-Fi cercanos"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Permite que la app muestre anuncios, se conecte y determine la posición relativa de los dispositivos Wi-Fi cercanos"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Información sobre servicio de pago NFC preferido"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Permite que la app reciba información del servicio de pago NFC preferido, como el servicio de asistencia registrado y el destino de la ruta."</string> <string name="permlab_nfc" msgid="1904455246837674977">"controlar la Transmisión de datos en proximidad"</string> @@ -2432,54 +2436,30 @@ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Activar"</string> <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Atrás"</string> <string name="unarchival_session_app_label" msgid="6811856981546348205">"Pendiente…"</string> - <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) --> - <skip /> - <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) --> - <skip /> + <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"La función SOS por satélite ya está disponible"</string> + <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"Puedes enviar mensajes a los servicios de emergencia si no hay una red móvil o Wi-Fi. Tu app de mensajería predeterminada debe ser Mensajes de Google."</string> + <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"No se admite la función SOS por satélite"</string> + <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"Este dispositivo no admite la función SOS por satélite"</string> + <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"No se configuró la función SOS por satélite"</string> + <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"Asegúrate de tener conexión a Internet y vuelve a intentar la configuración"</string> + <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"La función SOS por satélite no está disponible"</string> + <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"La función SOS por satélite no está disponible en este país o región"</string> + <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"No se configuró la función SOS por satélite"</string> + <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"Para enviar mensajes por satélite, establece Mensajes de Google como tu app de mensajería predeterminada"</string> + <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"La función SOS por satélite no está disponible"</string> + <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"Para verificar si la función SOS por satélite está disponible en este país o región, activa la configuración de ubicación"</string> + <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"La Mensajería satelital está disponible"</string> + <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"Puedes enviar mensajes por satélite si no hay una red móvil o Wi-Fi. Tu app de mensajería predeterminada debe ser Mensajes de Google."</string> + <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"No se admite la Mensajería satelital"</string> + <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"Este dispositivo no admite la Mensajería satelital"</string> + <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"No se configuró la Mensajería satelital"</string> + <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"Asegúrate de tener conexión a Internet y vuelve a intentar la configuración"</string> + <string name="satellite_messaging_not_in_allowed_region_notification_title" msgid="2035303593479031655">"La Mensajería satelital no disponible"</string> + <string name="satellite_messaging_not_in_allowed_region_notification_summary" msgid="5270294879531815854">"La Mensajería satelital no está disponible en este país o región"</string> + <string name="satellite_messaging_unsupported_default_sms_app_notification_title" msgid="1004808759472360189">"No se configuró la Mensajería satelital"</string> + <string name="satellite_messaging_unsupported_default_sms_app_notification_summary" msgid="17084124893763593">"Para enviar mensajes por satélite, establece Mensajes de Google como tu app de mensajería predeterminada"</string> + <string name="satellite_messaging_location_disabled_notification_title" msgid="7270641894250928494">"La Mensajería satelital no disponible"</string> + <string name="satellite_messaging_location_disabled_notification_summary" msgid="1450824950686221810">"Para verificar si la Mensajería satelital está disponible en este país o región, activa la configuración de ubicación"</string> <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Vuelve a configurar el Desbloqueo con huellas dactilares"</string> <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"Ya no se puede reconocer <xliff:g id="FINGERPRINT">%s</xliff:g>."</string> <string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"Ya no se pueden reconocer <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> y <xliff:g id="FINGERPRINT_1">%2$s</xliff:g>."</string> @@ -2489,7 +2469,7 @@ <string name="face_dangling_notification_msg" msgid="746235263598985384">"Ya no se puede reconocer tu modelo de rostro. Vuelve a configurar el Desbloqueo facial."</string> <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Configurar"</string> <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Ahora no"</string> - <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"Alarma para: <xliff:g id="USER_NAME">%s</xliff:g>"</string> + <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"Alarma para <xliff:g id="USER_NAME">%s</xliff:g>"</string> <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"Cambiar de usuario"</string> <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"Silenciar"</string> <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"Presiona para silenciar el sonido"</string> diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml index 166e2dac1366..23e1ae635730 100644 --- a/core/res/res/values-es/strings.xml +++ b/core/res/res/values-es/strings.xml @@ -613,6 +613,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Permite que la aplicación determine la posición relativa de los dispositivos de banda ultraancha cercanos"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"interactuar con dispositivos Wi-Fi cercanos"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Permite a la aplicación emitir y conectarse a dispositivos Wi-Fi cercanos y determinar su posición relativa"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Información sobre el servicio de pago por NFC preferido"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Permite que la aplicación obtenga información sobre el servicio de pago por NFC preferido, como identificadores de aplicación registrados y destinos de rutas."</string> <string name="permlab_nfc" msgid="1904455246837674977">"controlar Comunicación de campo cercano (NFC)"</string> diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml index fed97a191c87..d22c93b30ac9 100644 --- a/core/res/res/values-et/strings.xml +++ b/core/res/res/values-et/strings.xml @@ -612,6 +612,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Võimaldab rakendusel määrata lähedalasuvate ülilairibaühendust kasutavate seadmete suhtelise kauguse üksteisest"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"Läheduses olevate WiFi-seadmetega suhtlemine"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Lubab rakendusel läheduses olevatele WiFi-seadmetele reklaamida, nendega ühenduse luua ja määrata nende suhteline asend"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Eelistatud NFC-makseteenuse teave"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Võimaldab rakendusel hankida eelistatud NFC-makseteenuse teavet (nt registreeritud abi ja marsruudi sihtkoht)."</string> <string name="permlab_nfc" msgid="1904455246837674977">"lähiväljaside juhtimine"</string> diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml index 26e41140f391..914dfb72c06e 100644 --- a/core/res/res/values-eu/strings.xml +++ b/core/res/res/values-eu/strings.xml @@ -612,6 +612,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Banda ultrazabala darabilten inguruko gailuen arteko distantzia erlatiboa zehazteko baimena ematen dio aplikazioari"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"inguruko wifi-gailuekin interakzioan jardun"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Inguruko wifi-gailuetan iragartzeko, haiekin konektatzeko eta haien kokapena zehazteko baimena ematen dio aplikazioari"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"NFC bidezko ordainketa-zerbitzu lehenetsiari buruzko informazioa"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"NFC bidezko ordainketa-zerbitzu lehenetsiari buruzko informazioa jasotzeko baimena ematen dio aplikazioari, hala nola erregistratutako laguntzaileak eta ibilbidearen helmuga."</string> <string name="permlab_nfc" msgid="1904455246837674977">"kontrolatu Near Field Communication komunikazioa"</string> diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml index a8659eb289de..c3e4b4680752 100644 --- a/core/res/res/values-fa/strings.xml +++ b/core/res/res/values-fa/strings.xml @@ -612,6 +612,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"به برنامه اجازه داده میشود موقعیت نسبی بین دستگاههای «فراپهنباند» اطراف را مشخص کند"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"برقراری تعامل با دستگاههای Wi-Fi اطراف"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"به برنامه اجازه میدهد در دستگاههای Wi-Fi اطراف تبلیغ کند، به آنها متصل شود، و موقعیت نسبی آنها را تشخیص دهد"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"اطلاعات ترجیحی سرویس پرداخت NFC"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"به برنامه اجازه میدهد اطلاعات ترجیحی سرویس پرداخت NFC، مانند کمکهای ثبتشده و مقصد مسیر را دریافت کند."</string> <string name="permlab_nfc" msgid="1904455246837674977">"کنترل ارتباط راه نزدیک"</string> diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml index 1f4372abb677..fb7c4fed3f47 100644 --- a/core/res/res/values-fi/strings.xml +++ b/core/res/res/values-fi/strings.xml @@ -612,6 +612,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Sallii sovelluksen määrittää UVB-taajuutta käyttävien laitteiden sijainnin suhteessa toisiinsa"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"käyttää lähellä olevia Wi-Fi-laitteita"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Sallii sovelluksen ilmoittaa ja määrittää lähellä olevien Wi-Fi-laitteiden suhteellisen sijainnin sekä yhdistää niihin"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Ensisijaiset NFC-maksupalvelutiedot"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Sallii sovelluksen noutaa tietoja rekisteröidyistä sovellustunnuksista, maksureitin kohteesta ja muita ensisijaisia NFC-maksupalvelutietoja."</string> <string name="permlab_nfc" msgid="1904455246837674977">"hallitse Near Field Communication -tunnistusta"</string> diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml index f7f1474fca7b..18ef10995e90 100644 --- a/core/res/res/values-fr-rCA/strings.xml +++ b/core/res/res/values-fr-rCA/strings.xml @@ -613,6 +613,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Autorisez l\'appli à déterminer la position relative entre des appareils à bande ultralarge à proximité"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"interagir avec les appareils Wi-Fi à proximité"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Permet à l\'appli de diffuser des annonces, de se connecter et de déterminer la position relative des appareils Wi-Fi à proximité"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Information sur le service préféré de paiement CCP"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Permet à l\'appli d\'obtenir de l\'information sur le service préféré de paiement CCP comme les aides enregistrées et la route de destination."</string> <string name="permlab_nfc" msgid="1904455246837674977">"gérer la communication en champ proche"</string> @@ -2432,54 +2436,30 @@ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Activer"</string> <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Retour"</string> <string name="unarchival_session_app_label" msgid="6811856981546348205">"En attente…"</string> - <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) --> - <skip /> - <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) --> - <skip /> + <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"SOS par satellite est maintenant accessible"</string> + <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"Vous pouvez envoyer un message aux services d\'urgence s\'il n\'y a pas de réseau mobile ou Wi-Fi. Messages de Google doit être votre appli de messagerie par défaut."</string> + <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"SOS par satellite n\'est pas prise en charge"</string> + <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"SOS par satellite n\'est pas prise en charge sur cet appareil"</string> + <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"SOS par satellite n\'est pas configurée"</string> + <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"Assurez-vous que vous êtes connecté à Internet et réessayez d\'effectuer la configuration"</string> + <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"SOS par satellite n\'est pas accessible"</string> + <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"SOS par satellite n\'est pas accessible dans ce pays ou cette région"</string> + <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"SOS par satellite n\'est pas configurée"</string> + <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"Pour envoyer des messages par satellite, définissez Messages de Google comme appli de messagerie par défaut"</string> + <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"SOS par satellite n\'est pas accessible"</string> + <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"Pour vérifier si SOS par satellite est accessible dans ce pays ou cette région, activez les paramètres de localisation"</string> + <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"La messagerie par satellite est accessible"</string> + <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"Vous pouvez envoyer des messages par satellite s\'il n\'y a pas de réseau mobile ou Wi-Fi. Messages de Google doit être votre appli de messagerie par défaut."</string> + <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"La messagerie par satellite n\'est pas prise en charge"</string> + <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"La messagerie par satellite n\'est pas prise en charge sur cet appareil"</string> + <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"La messagerie par satellite n\'est pas configurée"</string> + <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"Assurez-vous que vous êtes connecté à Internet et réessayez d\'effectuer la configuration"</string> + <string name="satellite_messaging_not_in_allowed_region_notification_title" msgid="2035303593479031655">"La messagerie par satellite n\'est pas accessible"</string> + <string name="satellite_messaging_not_in_allowed_region_notification_summary" msgid="5270294879531815854">"La messagerie par satellite n\'est pas accessible dans ce pays ou cette région"</string> + <string name="satellite_messaging_unsupported_default_sms_app_notification_title" msgid="1004808759472360189">"La messagerie par satellite n\'est pas configurée"</string> + <string name="satellite_messaging_unsupported_default_sms_app_notification_summary" msgid="17084124893763593">"Pour envoyer des messages par satellite, définissez Messages de Google comme appli de messagerie par défaut"</string> + <string name="satellite_messaging_location_disabled_notification_title" msgid="7270641894250928494">"La messagerie par satellite n\'est pas accessible"</string> + <string name="satellite_messaging_location_disabled_notification_summary" msgid="1450824950686221810">"Pour vérifier si la messagerie par satellite est accessible dans ce pays ou cette région, activez les paramètres de localisation"</string> <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Configurer le Déverrouillage par empreinte digitale à nouveau"</string> <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"L\'empreinte digitale <xliff:g id="FINGERPRINT">%s</xliff:g> ne peut plus être reconnue."</string> <string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"Les empreintes digitales <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> et <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> ne peuvent plus être reconnues."</string> diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml index bed2927867c4..bd064d1dc61e 100644 --- a/core/res/res/values-fr/strings.xml +++ b/core/res/res/values-fr/strings.xml @@ -613,6 +613,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Autoriser l\'appli à déterminer la position relative entre des appareils ultra-wideband à proximité"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"interagir avec les appareils Wi-Fi à proximité"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Permet à l\'appli de déterminer la position approximative des appareils Wi‑Fi à proximité, de les afficher et de s\'y connecter"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informations sur le service de paiement NFC préféré"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Permet à l\'application d\'obtenir des informations sur le service de paiement NFC préféré, y compris les ID d\'applications et les destinations de routage enregistrés."</string> <string name="permlab_nfc" msgid="1904455246837674977">"contrôler la communication en champ proche"</string> diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml index 532c07880100..292a952b1d46 100644 --- a/core/res/res/values-gl/strings.xml +++ b/core/res/res/values-gl/strings.xml @@ -612,6 +612,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Permite que a aplicación determine a posición relativa entre os dispositivos próximos que usen banda ultralarga"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"interactuar con dispositivos wifi próximos"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Permítelle á aplicación enviar anuncios e conectarse a dispositivos wifi próximos, e determinar a súa posición relativa"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Información do servizo de pagos de NFC preferido"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Permite que a aplicación obteña información do servizo de pagos de NFC preferido, como as axudas rexistradas e o destino da ruta."</string> <string name="permlab_nfc" msgid="1904455246837674977">"controlar Near Field Communication"</string> diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml index 7e36c616e816..70454a307791 100644 --- a/core/res/res/values-gu/strings.xml +++ b/core/res/res/values-gu/strings.xml @@ -612,6 +612,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"ઍપને નજીકના અલ્ટ્રા-વાઇડબૅન્ડ ડિવાઇસની વચ્ચેનું સંબંધિત અંતર નક્કી કરવાની મંજૂરી આપો"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"નજીકના વાઇ-ફાઇ ડિવાઇસ સાથે ક્રિયાપ્રતિક્રિયા કરો"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"ઍપને નજીકના વાઇ-ફાઇ ડિવાઇસની માહિતી બતાવવાની, તેની સાથે કનેક્ટ કરવાની અને તેની સંબંધિત સ્થિતિ નક્કી કરવાની મંજૂરી આપો"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"પસંદગીની NFC ચુકવણીની સેવા વિશે માહિતી"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"આ મંજૂરીને આપવાથી, ઍપ તમારી પસંદગીની NFC ચુકવણીની સેવા વિશે માહિતી મેળવી શકે છે, જેમ કે રજિસ્ટર થયેલી સહાય અને નિર્ધારિત સ્થાન."</string> <string name="permlab_nfc" msgid="1904455246837674977">"નિઅર ફીલ્ડ કમ્યુનિકેશન નિયંત્રિત કરો"</string> @@ -1663,7 +1667,7 @@ <string name="default_audio_route_name_headphones" msgid="6954070994792640762">"હેડફોન"</string> <string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string> <string name="default_audio_route_category_name" msgid="5241740395748134483">"સિસ્ટમ"</string> - <string name="bluetooth_a2dp_audio_route_name" msgid="4214648773120426288">"બ્લૂટૂથ ઑડિઓ"</string> + <string name="bluetooth_a2dp_audio_route_name" msgid="4214648773120426288">"બ્લૂટૂથ ઑડિયો"</string> <string name="wireless_display_route_description" msgid="8297563323032966831">"વાયરલેસ ડિસ્પ્લે"</string> <string name="media_route_button_content_description" msgid="2299223698196869956">"કાસ્ટ કરો"</string> <string name="media_route_chooser_title" msgid="6646594924991269208">"ઉપકરણ સાથે કનેક્ટ કરો"</string> @@ -2058,7 +2062,7 @@ <string name="conference_call" msgid="5731633152336490471">"કોન્ફરન્સ કૉલ"</string> <string name="tooltip_popup_title" msgid="7863719020269945722">"ટૂલટિપ"</string> <string name="app_category_game" msgid="4534216074910244790">"રમતો"</string> - <string name="app_category_audio" msgid="8296029904794676222">"સંગીત અને ઑડિયો"</string> + <string name="app_category_audio" msgid="8296029904794676222">"મ્યુઝિક અને ઑડિયો"</string> <string name="app_category_video" msgid="2590183854839565814">"મૂવી અને વીડિઓ"</string> <string name="app_category_image" msgid="7307840291864213007">"ફોટો અને છબીઓ"</string> <string name="app_category_social" msgid="2278269325488344054">"સામાજિક અને સંચાર"</string> diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml index 38e79b1988dc..5422f1befc40 100644 --- a/core/res/res/values-hi/strings.xml +++ b/core/res/res/values-hi/strings.xml @@ -612,6 +612,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"ऐप्लिकेशन को आस-पास मौजूद Ultra-Wideband डिवाइसों के बीच की दूरी का पता लगाने की अनुमति दें"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"आस-पास मौजूद वाई-फ़ाई डिवाइसों से इंटरैक्ट करें"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"इससे, ऐप्लिकेशन आस-पास मौजूद वाई-फ़ाई डिवाइसों की जानकारी दिखा पाएगा, उनसे कनेक्ट कर पाएगा, और उनकी दूरी पता लगा पाएगा"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"NFC का इस्तेमाल करने वाली पैसे चुकाने की पसंदीदा सेवा की जानकारी"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"अगर ऐप्लिकेशन को अनुमति दी जाती है, तो वह पैसे चुकाने की आपकी उस पसंदीदा सेवा के बारे में जानकारी पा सकता है जो NFC का इस्तेमाल करती है. इसमें रजिस्टर किए गए डिवाइस और उनके आउटपुट के रूट जैसी जानकारी शामिल होती है."</string> <string name="permlab_nfc" msgid="1904455246837674977">"नियर फ़ील्ड कम्यूनिकेशन नियंत्रित करें"</string> @@ -2431,54 +2435,30 @@ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"चालू करें"</string> <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"रद्द करें"</string> <string name="unarchival_session_app_label" msgid="6811856981546348205">"प्रोसेस जारी है..."</string> - <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) --> - <skip /> - <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) --> - <skip /> + <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"सैटलाइट एसओएस की सुविधा अब उपलब्ध है"</string> + <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"मोबाइल या वाई-फ़ाई नेटवर्क न होने पर भी, आपातकालीन सेवाओं को मैसेज भेजा जा सकता है. इसके लिए, आपको Google Messages को अपने डिवाइस के डिफ़ॉल्ट मैसेजिंग ऐप्लिकेशन के तौर पर सेट करना होगा."</string> + <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"सैटलाइट एसओएस की सुविधा उपलब्ध नहीं है"</string> + <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"इस डिवाइस पर सैटलाइट एसओएस की सुविधा काम नहीं करती"</string> + <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"सैटलाइट एसओएस सेट अप नहीं किया गया है"</string> + <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"पक्का करें कि आपका डिवाइस, इंटरनेट से कनेक्ट है या नहीं. इसके बाद, फिर से सेटअप करने की कोशिश करें"</string> + <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_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> + <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"मोबाइल या वाई-फ़ाई नेटवर्क न होने पर भी, सैटलाइट के ज़रिए मैसेज भेजा जा सकता है. इसके लिए, आपको Google Messages को अपने डिवाइस के डिफ़ॉल्ट मैसेजिंग ऐप्लिकेशन के तौर पर सेट करना होगा."</string> + <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"सैटलाइट के ज़रिए मैसेज भेजने की सुविधा काम नहीं करती"</string> + <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"इस डिवाइस पर सैटलाइट के ज़रिए मैसेज भेजने की सुविधा काम नहीं करती"</string> + <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"सैटलाइट के ज़रिए मैसेज भेजने की सुविधा सेट अप नहीं की गई"</string> + <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"पक्का करें कि आपका डिवाइस, इंटरनेट से कनेक्ट है या नहीं. इसके बाद, फिर से सेटअप करने की कोशिश करें"</string> + <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_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> <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-hr/strings.xml b/core/res/res/values-hr/strings.xml index 90e228e65afa..0594788fa9ce 100644 --- a/core/res/res/values-hr/strings.xml +++ b/core/res/res/values-hr/strings.xml @@ -613,6 +613,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Dopušta aplikaciji da odredi približni položaj između uređaja u blizini koji upotrebljavaju ultraširokopojasno povezivanje"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"interakcija s Wi-Fi uređajima u blizini"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Aplikaciji omogućuje oglašavanje, povezivanje i određivanje približnog položaja Wi-Fi uređaja u blizini"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informacije o preferiranoj usluzi plaćanja NFC"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Omogućuje aplikaciji primanje informacija o preferiranoj usluzi plaćanja NFC kao što su registrirana pomagala i odredište."</string> <string name="permlab_nfc" msgid="1904455246837674977">"upravljanje beskontaktnom komunikacijom (NFC)"</string> @@ -2432,54 +2436,30 @@ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Uključi"</string> <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Natrag"</string> <string name="unarchival_session_app_label" msgid="6811856981546348205">"Na čekanju..."</string> - <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) --> - <skip /> - <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) --> - <skip /> + <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"SOS putem satelita sad je dostupan"</string> + <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"Možete slati poruke hitnim službama ako mobilna ili Wi-Fi mreža nije dostupna. Google poruke moraju biti vaša zadana aplikacija za slanje poruka."</string> + <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"SOS putem satelita nije podržan"</string> + <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"SOS putem satelita nije podržan na ovom uređaju"</string> + <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"SOS putem satelita nije postavljen"</string> + <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"Provjerite jeste li povezani s internetom i pokušajte ponovo s postavljanjem"</string> + <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"SOS putem satelita nije dostupan"</string> + <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"SOS putem satelita nije dostupan u ovoj državi ili regiji"</string> + <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"SOS putem satelita nije postavljen"</string> + <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"Da biste slali poruke putem satelita, postavite Google poruke kao zadanu aplikaciju za slanje poruka"</string> + <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"SOS putem satelita nije dostupan"</string> + <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"Da biste provjerili je li SOS putem satelita dostupan u ovoj državi ili regiji, uključite postavke lokacije"</string> + <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"Slanje poruka putem satelita je dostupno"</string> + <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"Možete slati poruke putem satelita ako mobilna ili Wi-Fi mreža nije dostupna. Google poruke moraju biti vaša zadana aplikacija za slanje poruka."</string> + <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"Slanje poruka putem satelita nije podržano"</string> + <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"Slanje poruka putem satelita nije podržano na ovom uređaju"</string> + <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"Slanje poruka putem satelita nije postavljeno"</string> + <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"Provjerite jeste li povezani s internetom i pokušajte ponovo s postavljanjem"</string> + <string name="satellite_messaging_not_in_allowed_region_notification_title" msgid="2035303593479031655">"Slanje poruka putem satelita nije dostupno"</string> + <string name="satellite_messaging_not_in_allowed_region_notification_summary" msgid="5270294879531815854">"Slanje poruka putem satelita nije dostupno u ovoj državi ili regiji"</string> + <string name="satellite_messaging_unsupported_default_sms_app_notification_title" msgid="1004808759472360189">"Slanje poruka putem satelita nije postavljeno"</string> + <string name="satellite_messaging_unsupported_default_sms_app_notification_summary" msgid="17084124893763593">"Da biste slali poruke putem satelita, postavite Google poruke kao zadanu aplikaciju za slanje poruka"</string> + <string name="satellite_messaging_location_disabled_notification_title" msgid="7270641894250928494">"Slanje poruka putem satelita nije dostupno"</string> + <string name="satellite_messaging_location_disabled_notification_summary" msgid="1450824950686221810">"Da biste provjerili je li slanje poruka putem satelita dostupno u ovoj državi ili regiji, uključite postavke lokacije"</string> <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Ponovno postavite otključavanje otiskom prsta"</string> <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> više se ne prepoznaje."</string> <string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> i <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> više se ne prepoznaju."</string> diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml index d5a28f8e2198..8adb234830aa 100644 --- a/core/res/res/values-hu/strings.xml +++ b/core/res/res/values-hu/strings.xml @@ -612,6 +612,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Az alkalmazás meghatározhatja a közeli, ultraszélessávú eszközök közötti relatív pozíciót"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"műveletek végrehajtása a közeli Wi‑Fi-eszközökkel"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Engedélyezi az alkalmazás számára, hogy közzétegye és meghatározza a közeli Wi-Fi-eszközök viszonylagos helyzetét, és csatlakozzon hozzájuk."</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Preferált NFC fizetési szolgáltatási információk"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Lehetővé teszi az alkalmazás számára preferált NFC fizetési szolgáltatási információk (pl. regisztrált alkalmazásazonosítók és útvonali cél) lekérését."</string> <string name="permlab_nfc" msgid="1904455246837674977">"NFC technológia vezérlése"</string> diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml index 14534a7ca2a8..4a16d2f37194 100644 --- a/core/res/res/values-hy/strings.xml +++ b/core/res/res/values-hy/strings.xml @@ -612,6 +612,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Թույլատրել հավելվածին որոշել գերլայնաշերտ կապի տեխնոլոգիան աջակցող մոտակա սարքերի միջև հարաբերական դիրքավորումը"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"փոխներգործել մոտակա Wi‑Fi սարքերի հետ"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Թույլ է տալիս հավելվածին տվյալներ փոխանցել մոտակա Wi‑Fi սարքերին, միանալ դրանց և որոշել դրանց մոտավոր դիրքը։"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Տեղեկություններ NFC վճարային ծառայության մասին"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Թույլ է տալիս հավելվածին ստանալ նախընտրելի NFC վճարային ծառայության մասին տեղեկություններ (օր․՝ գրանցված լրացուցիչ սարքերի և երթուղու նպատակակետի մասին տվյալներ)։"</string> <string name="permlab_nfc" msgid="1904455246837674977">"վերահսկել Մոտ Տարածությամբ Հաղորդակցումը"</string> diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml index 360102f13139..e90137fd761f 100644 --- a/core/res/res/values-in/strings.xml +++ b/core/res/res/values-in/strings.xml @@ -612,6 +612,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Mengizinkan aplikasi menentukan posisi relatif antar-perangkat Ultra-Wideband di sekitar"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"berinteraksi dengan perangkat Wi-Fi di sekitar"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Mengizinkan aplikasi menampilkan, menghubungkan, dan menentukan posisi relatif perangkat Wi-Fi di sekitar"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informasi Layanan Pembayaran NFC Pilihan"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Mengizinkan aplikasi untuk mendapatkan informasi layanan pembayaran NFC pilihan seperti bantuan terdaftar dan tujuan rute."</string> <string name="permlab_nfc" msgid="1904455246837674977">"kontrol NFC"</string> diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml index 9d3e2e42fb11..9ca5731ad1eb 100644 --- a/core/res/res/values-is/strings.xml +++ b/core/res/res/values-is/strings.xml @@ -612,6 +612,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Leyfa forritinu að ákvarða fjarlægð milli nálægra tækja með ofurbreiðband"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"eiga í samskiptum við nálæg WiFi-tæki"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Leyfir forritinu að auglýsa, tengja og áætla staðsetningu nálægra WiFi-tækja"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Upplýsingar um valda NFC-greiðsluþjónustu"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Gerir forritinu kleift að fá valda NFC-greiðsluþjónustu, svo sem skráða aðstoð og áfangastað leiðar."</string> <string name="permlab_nfc" msgid="1904455246837674977">"stjórna nándarsamskiptum (NFC)"</string> @@ -2431,54 +2435,30 @@ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Kveikja"</string> <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Til baka"</string> <string name="unarchival_session_app_label" msgid="6811856981546348205">"Í bið…"</string> - <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) --> - <skip /> - <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) --> - <skip /> + <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"Nú er gervihnattar-SOS tiltækt"</string> + <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"Þú getur sent neyðarþjónustu skilaboð, jafnvel þótt farsímakerfi eða WiFi-tenging sé ekki til staðar. Google Messages verður að vera stillt sem sjálfgefið skilaboðaforrit."</string> + <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"Gervihnattar-SOS er ekki stutt"</string> + <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"Gervihnattar-SOS er ekki stutt í þessu tæki"</string> + <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"Gervihnattar-SOS er ekki uppsett"</string> + <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"Gakktu úr skugga um að þú sért með nettengingu og reyndu uppsetningu aftur"</string> + <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"Gervihnattar-SOS er ekki tiltækt"</string> + <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"Gervihnattar-SOS er ekki tiltækt í þessu landi eða landsvæði"</string> + <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"Gervihnattar-SOS er ekki uppsett"</string> + <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"Til að senda skilaboð í gegnum gervihnött skaltu stilla Google Messages sem sjálfgefið skilaboðaforrit"</string> + <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"Gervihnattar-SOS er ekki tiltækt"</string> + <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"Til að athuga hvort gervihnattar-SOS eru tiltæk í þessu landi eða á þessu svæði skaltu kveikja á staðsetningarstillingum"</string> + <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"Skilaboð í gegnum gervihnött eru tiltæk"</string> + <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"Þú getur sent skilaboð um gervihnött ef það er ekkert farsímakerfi eða WiFi-net. Google Messages verður að vera stillt sem sjálfgefið skilaboðaforrit."</string> + <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"Skilaboð í gegnum gervihnött eru ekki studd"</string> + <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"Skilaboð í gegnum gervihnött eru ekki studd í þessu tæki"</string> + <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"Skilaboð í gegnum gervihnött eru ekki uppsett"</string> + <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"Gakktu úr skugga um að þú sért með nettengingu og reyndu uppsetningu aftur"</string> + <string name="satellite_messaging_not_in_allowed_region_notification_title" msgid="2035303593479031655">"Skilaboð í gegnum gervihnött eru ekki tiltæk"</string> + <string name="satellite_messaging_not_in_allowed_region_notification_summary" msgid="5270294879531815854">"Skilaboð í gegnum gervihnött eru ekki tiltæk í þessu landi eða á þessu svæði"</string> + <string name="satellite_messaging_unsupported_default_sms_app_notification_title" msgid="1004808759472360189">"Skilaboð í gegnum gervihnött eru ekki uppsett"</string> + <string name="satellite_messaging_unsupported_default_sms_app_notification_summary" msgid="17084124893763593">"Til að senda skilaboð í gegnum gervihnött skaltu stilla Google Messages sem sjálfgefið skilaboðaforrit"</string> + <string name="satellite_messaging_location_disabled_notification_title" msgid="7270641894250928494">"Skilaboð í gegnum gervihnött eru ekki tiltæk"</string> + <string name="satellite_messaging_location_disabled_notification_summary" msgid="1450824950686221810">"Til að athuga hvort skilaboð í gegnum gervihnött eru tiltæk í þessu landi eða á þessu svæði skaltu kveikja á staðsetningarstillingum"</string> <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Setja upp fingrafarskenni aftur"</string> <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"Ekki er hægt að bera kennsl á <xliff:g id="FINGERPRINT">%s</xliff:g> lengur."</string> <string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"Ekki er hægt að bera kennsl á <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> og <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> lengur."</string> @@ -2488,7 +2468,7 @@ <string name="face_dangling_notification_msg" msgid="746235263598985384">"Ekki er hægt að bera kennsl á andlitslíkanið þitt lengur. Settu upp andlitskenni aftur."</string> <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Setja upp"</string> <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Ekki núna"</string> - <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"Viðvörun fyrir: <xliff:g id="USER_NAME">%s</xliff:g>"</string> + <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"Viðvörun: <xliff:g id="USER_NAME">%s</xliff:g>"</string> <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"Skipta um notanda"</string> <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"Þagga"</string> <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"Ýttu til að þagga hljóð"</string> diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml index ee7f08db3e51..ac0e4580b7a0 100644 --- a/core/res/res/values-it/strings.xml +++ b/core/res/res/values-it/strings.xml @@ -613,6 +613,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Consenti all\'app di stabilire la posizione relativa tra dispositivi a banda ultralarga nelle vicinanze"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"Interazione con dispositivi Wi-Fi vicini"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Consente all\'app di trasmettere annunci e connettersi a dispositivi Wi‑Fi vicini e di stabilirne la posizione relativa."</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informazioni del servizio di pagamento NFC preferito"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Consente all\'app di recuperare informazioni del servizio di pagamento NFC preferito, quali destinazione della route e identificatori applicazione registrati."</string> <string name="permlab_nfc" msgid="1904455246837674977">"controllo Near Field Communication"</string> @@ -2489,7 +2493,7 @@ <string name="face_dangling_notification_msg" msgid="746235263598985384">"Il tuo modello del volto non può più essere riconosciuto. Riconfigura lo Sblocco con il Volto."</string> <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Configura"</string> <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Non ora"</string> - <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"Sveglia per: <xliff:g id="USER_NAME">%s</xliff:g>"</string> + <string name="bg_user_sound_notification_title_alarm" msgid="5251678483393143527">"Sveglia per <xliff:g id="USER_NAME">%s</xliff:g>"</string> <string name="bg_user_sound_notification_button_switch_user" msgid="3091969648572788946">"Cambia utente"</string> <string name="bg_user_sound_notification_button_mute" msgid="4942158515665615243">"Disattiva audio"</string> <string name="bg_user_sound_notification_message" msgid="8613881975316976673">"Tocca per disattivare l\'audio"</string> diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml index e3f2a7cab5eb..386f5318f53a 100644 --- a/core/res/res/values-iw/strings.xml +++ b/core/res/res/values-iw/strings.xml @@ -613,6 +613,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"האפליקציה תזהה את המיקום היחסי בין מכשירים קרובים שמשדרים בטכנולוגיית Ultra Wideband (UWB)"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"אינטראקציה עם מכשירי Wi-Fi בקרבת מקום"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"האפליקציה תוכל לפרסם במכשירי Wi-Fi בקרבת מקום, להתחבר אליהם ולהעריך את המיקום היחסי שלהם"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"פרטים על שירות תשלום מועדף ב-NFC"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"מאפשרת לאפליקציה לקבל פרטים על שירות תשלום מועדף ב-NFC, כמו עזרים רשומים ויעד של נתיב."</string> <string name="permlab_nfc" msgid="1904455246837674977">"שליטה בתקשורת מטווח קצר"</string> @@ -2432,54 +2436,30 @@ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"הפעלה"</string> <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"חזרה"</string> <string name="unarchival_session_app_label" msgid="6811856981546348205">"בהמתנה..."</string> - <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) --> - <skip /> - <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) --> - <skip /> + <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"התכונה \"תקשורת לוויינית למצב חירום\" זמינה עכשיו"</string> + <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"אפשר לשלוח הודעות לשירותי החירום כשאין חיבור לרשת סלולרית או לרשת Wi-Fi. חובה להגדיר את Google Messages כאפליקציית ברירת המחדל להודעות."</string> + <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"התכונה \"תקשורת לוויינית למצב חירום\" לא נתמכת"</string> + <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"המכשיר הזה לא תומך בתכונה \"תקשורת לוויינית למצב חירום\""</string> + <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"התכונה \"תקשורת לוויינית למצב חירום\" לא מוגדרת"</string> + <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"צריך לוודא שיש חיבור לאינטרנט ולנסות שוב את תהליך ההגדרה"</string> + <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_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> + <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> + <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"המכשיר הזה לא תומך בתכונה \"העברת הודעות באמצעות לוויין\""</string> + <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"התכונה \"העברת הודעות באמצעות לוויין\" לא מוגדרת"</string> + <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"צריך לוודא שיש חיבור לאינטרנט ולנסות שוב את תהליך ההגדרה"</string> + <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_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> <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-ja/strings.xml b/core/res/res/values-ja/strings.xml index 0d3a80c9b69a..339144611afd 100644 --- a/core/res/res/values-ja/strings.xml +++ b/core/res/res/values-ja/strings.xml @@ -612,6 +612,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"付近の Ultra Wideband デバイス間の相対位置の特定をアプリに許可します"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"付近の Wi-Fi デバイスとの通信"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"付近の Wi-Fi デバイスについて、情報の表示、接続、相対位置の確認をアプリに許可します"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"優先される NFC お支払いサービスの情報"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"登録されている支援やルートの目的地など、優先される NFC お支払いサービスの情報を取得することをアプリに許可します。"</string> <string name="permlab_nfc" msgid="1904455246837674977">"NFCの管理"</string> @@ -2431,54 +2435,30 @@ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"ON にする"</string> <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"戻る"</string> <string name="unarchival_session_app_label" msgid="6811856981546348205">"保留中..."</string> - <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) --> - <skip /> - <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) --> - <skip /> + <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"衛星 SOS を利用できるようになりました"</string> + <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"モバイル ネットワークや Wi-Fi ネットワークがなくても、緊急サービスにメッセージを送信できます。Google メッセージがデフォルトのメッセージ アプリに設定されている必要があります。"</string> + <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"衛星 SOS に対応していません"</string> + <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"このデバイスは衛星 SOS に対応していません"</string> + <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"衛星 SOS が設定されていません"</string> + <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"インターネットに接続していることを確認してから、もう一度設定してみてください"</string> + <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"衛星 SOS を利用できません"</string> + <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"この国または地域では衛星 SOS を利用できません"</string> + <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"衛星 SOS が設定されていません"</string> + <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"衛星経由でメッセージを送受信するには、Google メッセージをデフォルトのメッセージ アプリに設定してください"</string> + <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"衛星 SOS を利用できません"</string> + <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"この国または地域で衛星 SOS を利用できるかどうかを確認するには、位置情報の設定を ON にしてください"</string> + <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"衛星通信メッセージを利用できます"</string> + <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"モバイル ネットワークや Wi-Fi ネットワークがなくても、衛星経由でメッセージを送受信できます。Google メッセージがデフォルトのメッセージ アプリに設定されている必要があります。"</string> + <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"衛星通信メッセージに対応していません"</string> + <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"このデバイスは衛星通信メッセージに対応していません"</string> + <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"衛星通信メッセージが設定されていません"</string> + <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"インターネットに接続していることを確認してから、もう一度設定してみてください"</string> + <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 メッセージをデフォルトのメッセージ アプリに設定してください"</string> + <string name="satellite_messaging_location_disabled_notification_title" msgid="7270641894250928494">"衛星通信メッセージを利用できません"</string> + <string name="satellite_messaging_location_disabled_notification_summary" msgid="1450824950686221810">"この国または地域で衛星通信メッセージを利用できるかどうかを確認するには、位置情報の設定を ON にしてください"</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-ka/strings.xml b/core/res/res/values-ka/strings.xml index 19177d65a991..58f78901ef5e 100644 --- a/core/res/res/values-ka/strings.xml +++ b/core/res/res/values-ka/strings.xml @@ -612,6 +612,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"ნებას რთავს აპს, დაადგინოს შედარებითი პოზიცია ახლომახლო ულტრაფართო სიხშირის მოწყობილობების შესახებ"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"ინტერაქცია ახლომახლო Wi-Fi მოწყობილობებთან"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"საშუალებას აძლევს აპს, განაცხადოს ახლომახლო Wi-Fi მოწყობილობების შესახებ, დაუკავშირდეს მათ და განსაზღვროს მათი შედარებითი პოზიცია"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"უპირატესი NFC გადახდის სერვისის ინფორმაცია"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"საშუალებას აძლევს აპს, მიიღოს უპირატესი NFC გადახდის სერვისის ინფორმაცია, მაგალითად, რეგისტრირებული დახმარება და დანიშნულება."</string> <string name="permlab_nfc" msgid="1904455246837674977">"ახლო მოქმედების რადიოკავშირი (NFC) მართვა"</string> @@ -2431,54 +2435,30 @@ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"ჩართვა"</string> <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"უკან დაბრუნება"</string> <string name="unarchival_session_app_label" msgid="6811856981546348205">"მომლოდინე..."</string> - <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) --> - <skip /> - <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) --> - <skip /> + <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"სატელიტური SOS ახლა ხელმისაწვდომია"</string> + <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"შეგიძლიათ, შეტყობინება გაუგზავნოთ გადაუდებელი დახმარების სამსახურებს, თუ მობილური ან Wi-Fi ქსელი არ არის ხელმისაწვდომი. საჭიროა, რომ Google Messages იყოს თქვენი ნაგულისხმევი შეტყობინებების აპი."</string> + <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"სატელიტური SOS არ არის მხარდაჭერილი"</string> + <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"სატელიტური SOS არ არის მხარდაჭერილი ამ მოწყობილობაზე"</string> + <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"სატელიტური SOS არ არის დაყენებული"</string> + <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"დარწმუნდით, რომ დაკავშირებული ხართ ინტერნეტთან და ცადეთ ხელახლა დაყენება"</string> + <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"სატელიტური SOS არ არის ხელმისაწვდომი"</string> + <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"სატელიტური SOS ამ ქვეყანასა თუ რეგიონში არ არის ხელმისაწვდომი"</string> + <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"სატელიტური SOS არ არის დაყენებული"</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">"სატელიტური SOS არ არის ხელმისაწვდომი"</string> + <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"ამ ქვეყანაში ან რეგიონში სატელიტური 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> + <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"ამ მოწყობილობაზე შეტყობინებების სატელიტური მიმოცვლა არ არის მხარდაჭერილი"</string> + <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"შეტყობინებების სატელიტური მიმოცვლა დაყენებული არ არის"</string> + <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"დარწმუნდით, რომ დაკავშირებული ხართ ინტერნეტთან და ცადეთ ხელახლა დაყენება"</string> + <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_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> <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-kk/strings.xml b/core/res/res/values-kk/strings.xml index 70774d6cafe1..737f950567a1 100644 --- a/core/res/res/values-kk/strings.xml +++ b/core/res/res/values-kk/strings.xml @@ -612,6 +612,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Қолданбаға маңайдағы кең жолақты құрылғылардың бір-біріне қатысты орнын анықтауға мүмкіндік береді."</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"маңайдағы Wi-Fi құрылғыларымен байланысу"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Қолданба маңайдағы Wi‑Fi құрылғыларына жарнама беріп, оларға қосылып, шамамен орналасқан жерін анықтай алады."</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Таңдаулы NFC төлеу қызметі туралы ақпарат"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Қолданба тіркелген көмектер және баратын жер маршруты сияқты таңдаулы NFC төлеу қызметі туралы ақпаратты ала алатын болады."</string> <string name="permlab_nfc" msgid="1904455246837674977">"NFC функциясын басқару"</string> diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml index c758e0501aa8..0ecad8f60fe1 100644 --- a/core/res/res/values-km/strings.xml +++ b/core/res/res/values-km/strings.xml @@ -612,6 +612,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"អនុញ្ញាតឱ្យកម្មវិធីកំណត់ចម្ងាយពាក់ព័ន្ធរវាងឧបករណ៍ Ultra-Wideband ដែលនៅជិត"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"ធ្វើអន្តរកម្មជាមួយឧបករណ៍ Wi‑Fi ដែលនៅជិត"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"អនុញ្ញាតឱ្យកម្មវិធីផ្សាយពាណិជ្ជកម្ម ភ្ជាប់ និងកំណត់ទីតាំងពាក់ព័ន្ធរបស់ឧបករណ៍ Wi‑Fi ដែលនៅជិត"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"ព័ត៌មានអំពីសេវាបង់ប្រាក់តាម NFC ជាអាទិភាព"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"អនុញ្ញាតឱ្យកម្មវិធីទទួលបានព័ត៌មានអំពីសេវាបង់ប្រាក់តាម nfc ជាអាទិភាពដូចជា គោលដៅផ្លូវ និងព័ត៌មានកំណត់អត្តសញ្ញាណកម្មវិធី ដែលបានចុះឈ្មោះជាដើម។"</string> <string name="permlab_nfc" msgid="1904455246837674977">"ពិនិត្យការទាក់ទងនៅក្បែរ (NFC)"</string> @@ -2431,54 +2435,30 @@ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"បើក"</string> <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"ថយក្រោយ"</string> <string name="unarchival_session_app_label" msgid="6811856981546348205">"កំពុងរង់ចាំ..."</string> - <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) --> - <skip /> - <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) --> - <skip /> + <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"ឥឡូវនេះ អាចប្រើការប្រកាសអាសន្នតាមផ្កាយរណបបានហើយ"</string> + <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"អ្នកអាចផ្ញើសារទៅសេវាសង្គ្រោះបន្ទាន់ ប្រសិនបើមិនមានបណ្ដាញឧបករណ៍ចល័ត ឬ Wi-Fi ទេ។ Google Messages ត្រូវតែជាកម្មវិធីផ្ញើសារលំនាំដើមរបស់អ្នក។"</string> + <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"មិនអាចប្រើការប្រកាសអាសន្នតាមផ្កាយរណបបានទេ"</string> + <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"មិនអាចប្រើការប្រកាសអាសន្នតាមផ្កាយរណបនៅលើឧបករណ៍នេះបានទេ"</string> + <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"ការប្រកាសអាសន្នតាមផ្កាយរណបមិនត្រូវបានរៀបចំទេ"</string> + <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"សូមប្រាកដថា អ្នកបានភ្ជាប់អ៊ីនធឺណិត រួចសាកល្បងរៀបចំម្ដងទៀត"</string> + <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_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> + <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> + <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"មិនអាចផ្ញើសារតាមផ្កាយរណបនៅលើឧបករណ៍នេះបានទេ"</string> + <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"មិនបានរៀបចំការផ្ញើសារតាមផ្កាយរណបទេ"</string> + <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"សូមប្រាកដថា អ្នកបានភ្ជាប់អ៊ីនធឺណិត រួចសាកល្បងរៀបចំម្ដងទៀត"</string> + <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_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> <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-kn/strings.xml b/core/res/res/values-kn/strings.xml index 8efcd0869303..c64f6ec1e7a7 100644 --- a/core/res/res/values-kn/strings.xml +++ b/core/res/res/values-kn/strings.xml @@ -612,6 +612,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"ಸಮೀಪದಲ್ಲಿರುವ ಅಲ್ಟ್ರಾ-ವೈಡ್ಬ್ಯಾಂಡ್ ಸಾಧನಗಳ ನಡುವೆ ಸಂಬಂಧಿತ ಸ್ಥಾನವನ್ನು ನಿರ್ಧರಿಸಲು ಆ್ಯಪ್ಗೆ ಅನುಮತಿಸಿ"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"ಹತ್ತಿರದ ವೈ -ಫೈ ಸಾಧನಗಳ ಜೊತೆಗೆ ಸಂವಹನ ನಡೆಸಿ"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"ಹತ್ತಿರದ ವೈ -ಫೈ ಸಾಧನಗಳ ಸಂಬಂಧಿತ ಸ್ಥಾನವನ್ನು ಸೂಚಿಸಲು, ಕನೆಕ್ಟ್ ಮಾಡಲು ಮತ್ತು ನಿರ್ಧರಿಸಲು ಆ್ಯಪ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"ಆದ್ಯತೆಯ NFC ಪಾವತಿ ಸೇವಾ ಮಾಹಿತಿ"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"ನೋಂದಾಯಿತ ಆ್ಯಪ್ ಗುರುತಿಸುವಿಕೆಗಳು ಮತ್ತು ಮಾರ್ಗ ಗಮ್ಯಸ್ಥಾನಗಳಂತಹ ಆದ್ಯತೆಯ NFC ಪಾವತಿ ಸೇವೆಗಳ ಬಗ್ಗೆ ಮಾಹಿತಿಯನ್ನು ಪಡೆಯಲು ಆ್ಯಪ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string> <string name="permlab_nfc" msgid="1904455246837674977">"ಸಮೀಪ ಕ್ಷೇತ್ರ ಸಂವಹನವನ್ನು ನಿಯಂತ್ರಿಸಿ"</string> @@ -2431,54 +2435,30 @@ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"ಆನ್ ಮಾಡಿ"</string> <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"ಹಿಂದಿರುಗಿ"</string> <string name="unarchival_session_app_label" msgid="6811856981546348205">"ಬಾಕಿ ಉಳಿದಿದೆ..."</string> - <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) --> - <skip /> - <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) --> - <skip /> + <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"ಸ್ಯಾಟಲೈಟ್ SOS ಈಗ ಲಭ್ಯವಿದೆ"</string> + <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"ಮೊಬೈಲ್ ಅಥವಾ ವೈ-ಫೈ ನೆಟ್ವರ್ಕ್ ಇಲ್ಲದಿದ್ದರೆ ನೀವು ತುರ್ತು ಸೇವೆಗಳಿಗೆ ಸಂದೇಶ ಕಳುಹಿಸಬಹುದು. Google Messages ನಿಮ್ಮ ಡೀಫಾಲ್ಟ್ ಮೆಸೇಜಿಂಗ್ ಆ್ಯಪ್ ಆಗಿರಬೇಕು."</string> + <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"ಸ್ಯಾಟಲೈಟ್ SOS ಬೆಂಬಲಿತವಾಗಿಲ್ಲ"</string> + <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"ಈ ಸಾಧನದಲ್ಲಿ ಸ್ಯಾಟಲೈಟ್ SOS ಬೆಂಬಲಿತವಾಗಿಲ್ಲ"</string> + <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"ಸ್ಯಾಟಲೈಟ್ SOS ಅನ್ನು ಸೆಟಪ್ ಮಾಡಲಾಗಿಲ್ಲ"</string> + <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"ನೀವು ಇಂಟರ್ನೆಟ್ಗೆ ಕನೆಕ್ಟ್ ಆಗಿರುವಿರಿ ಎಂಬುದನ್ನು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಿ ಮತ್ತು ಪುನಃ ಸೆಟಪ್ ಮಾಡಲು ಪ್ರಯತ್ನಿಸಿ"</string> + <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"ಸ್ಯಾಟಲೈಟ್ SOS ಲಭ್ಯವಿಲ್ಲ"</string> + <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"ಈ ದೇಶ ಅಥವಾ ಪ್ರದೇಶದಲ್ಲಿ ಸ್ಯಾಟಲೈಟ್ SOS ಲಭ್ಯವಿಲ್ಲ"</string> + <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"ಸ್ಯಾಟಲೈಟ್ SOS ಸೆಟಪ್ ಆಗಿಲ್ಲ"</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">"ಸ್ಯಾಟಲೈಟ್ SOS ಲಭ್ಯವಿಲ್ಲ"</string> + <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"ಈ ದೇಶ ಅಥವಾ ಪ್ರದೇಶದಲ್ಲಿ ಸ್ಯಾಟಲೈಟ್ SOS ಲಭ್ಯವಿದೆಯೇ ಎಂದು ಪರಿಶೀಲಿಸಲು, ಸ್ಥಳ ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು ಆನ್ ಮಾಡಿ"</string> + <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"ಸ್ಯಾಟಲೈಟ್ ಮೆಸೇಜಿಂಗ್ ಲಭ್ಯವಿದೆ"</string> + <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"ಮೊಬೈಲ್ ಅಥವಾ ವೈ-ಫೈ ನೆಟ್ವರ್ಕ್ ಇಲ್ಲದಿದ್ದರೆ ನೀವು ಸ್ಯಾಟಲೈಟ್ ಮೂಲಕ ಸಂದೇಶ ಕಳುಹಿಸಬಹುದು. Google Messages ನಿಮ್ಮ ಡೀಫಾಲ್ಟ್ ಮೆಸೇಜಿಂಗ್ ಆ್ಯಪ್ ಆಗಿರಬೇಕು."</string> + <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"ಸ್ಯಾಟಲೈಟ್ ಮೆಸೇಜಿಂಗ್ ಗೆ ಬೆಂಬಲವಿಲ್ಲ"</string> + <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"ಈ ಸಾಧನದಲ್ಲಿ ಸ್ಯಾಟಲೈಟ್ ಮೆಸೇಜಿಂಗ್ ಬೆಂಬಲಿಸುವುದಿಲ್ಲ"</string> + <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"ಸ್ಯಾಟಲೈಟ್ ಮೆಸೇಜಿಂಗ್ ಸೆಟಪ್ ಮಾಡಲಾಗಿಲ್ಲ"</string> + <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"ನೀವು ಇಂಟರ್ನೆಟ್ಗೆ ಕನೆಕ್ಟ್ ಆಗಿರುವಿರಿ ಎಂಬುದನ್ನು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಿ ಮತ್ತು ಪುನಃ ಸೆಟಪ್ ಮಾಡಲು ಪ್ರಯತ್ನಿಸಿ"</string> + <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_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> <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-ko/strings.xml b/core/res/res/values-ko/strings.xml index a069805148f5..d63d43e4cddd 100644 --- a/core/res/res/values-ko/strings.xml +++ b/core/res/res/values-ko/strings.xml @@ -612,6 +612,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"앱이 근처의 초광대역 기기 간 상대적 위치를 파악하도록 허용"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"근처 Wi‑Fi 기기와 상호작용"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"앱이 광역 신호를 보내 근처에 있는 Wi‑Fi 기기의 상대적인 위치를 확인하고 연결할 수 있도록 허용합니다."</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"기본 NFC 결제 서비스 정보"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"앱이 등록된 AID와 경로 목적지 같은 기본 NFC 결제 서비스 정보를 확인하도록 허용합니다."</string> <string name="permlab_nfc" msgid="1904455246837674977">"NFC(Near Field Communication) 제어"</string> @@ -2431,54 +2435,30 @@ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"사용"</string> <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"뒤로"</string> <string name="unarchival_session_app_label" msgid="6811856981546348205">"대기 중…"</string> - <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) --> - <skip /> - <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) --> - <skip /> + <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"이제 위성 긴급 SOS를 사용할 수 있음"</string> + <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"모바일 또는 Wi-Fi 네트워크가 없는 경우 응급 서비스로 메시지를 보낼 수 있습니다. Google 메시지가 기본 메시지 앱으로 설정되어 있어야 합니다."</string> + <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"위성 긴급 SOS가 지원되지 않음"</string> + <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"이 기기에서는 위성 긴급 SOS가 지원되지 않습니다."</string> + <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"위성 긴급 SOS가 설정되지 않음"</string> + <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"인터넷에 연결되어 있는지 확인한 후 다시 설정해 보세요."</string> + <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"위성 긴급 SOS를 사용할 수 없음"</string> + <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"이 국가 또는 지역에서는 위성 긴급 SOS를 사용할 수 없습니다."</string> + <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"위성 긴급 SOS가 설정되지 않음"</string> + <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"위성으로 메시지를 보내려면 Google 메시지를 기본 메시지 앱으로 설정하세요."</string> + <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"위성 긴급 SOS를 사용할 수 없음"</string> + <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"이 국가 또는 지역에서 위성 긴급 SOS를 사용할 수 있는지 확인하려면 위치 설정을 사용 설정하세요."</string> + <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"위성 메시지 사용 가능"</string> + <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"모바일 또는 Wi-Fi 네트워크가 없는 경우 위성으로 메시지를 보낼 수 있습니다. Google 메시지가 기본 메시지 앱으로 설정되어 있어야 합니다."</string> + <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"위성 메시지가 지원되지 않음"</string> + <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"이 기기에서는 위성 메시지가 지원되지 않습니다."</string> + <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"위성 메시지가 설정되지 않음"</string> + <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"인터넷에 연결되어 있는지 확인한 후 다시 설정해 보세요."</string> + <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 메시지를 기본 메시지 앱으로 설정하세요."</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> <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-ky/strings.xml b/core/res/res/values-ky/strings.xml index ec0059bb31f6..e09036a1b679 100644 --- a/core/res/res/values-ky/strings.xml +++ b/core/res/res/values-ky/strings.xml @@ -612,6 +612,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Колдонмо кең тилкелүү тармак аркылуу туташа турган жакын жердеги түзмөктөрдү аныктай алат"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"Жакын жердеги Wi‑Fi түзмөктөрүнө байланышуу"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Колдонмо жакын жердеги Wi-Fi түзмөктөргө туташып, алардын жайгашкан жерин аныктап, ар кандай нерселерди өткөрө алат."</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Тандалган NFC төлөм кызматы жөнүндө маалымат"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Колдонмого катталган жардам же көздөлгөн жерге маршрут сыяктуу тандалган nfc төлөм кызматы жөнүндө маалыматты алууга уруксат берүү."</string> <string name="permlab_nfc" msgid="1904455246837674977">"Near Field Communication көзөмөлү"</string> diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml index 3962f09f0bac..a9ebcd765502 100644 --- a/core/res/res/values-lo/strings.xml +++ b/core/res/res/values-lo/strings.xml @@ -612,6 +612,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"ອະນຸຍາດໃຫ້ແອັບກຳນົດຕຳແໜ່ງທີ່ສຳພັນກັນລະຫວ່າງອຸປະກອນ Ultra-Wideband ທີ່ຢູ່ໃກ້ຄຽງ"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"ໂຕ້ຕອບກັບອຸປະກອນ Wi‑Fi ທີ່ຢູ່ໃກ້ຄຽງ"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"ອະນຸຍາດໃຫ້ແອັບໂຄສະນາ, ເຊື່ອມຕໍ່ ແລະ ກຳນົດຕຳແໜ່ງສຳພັນຂອງອຸປະກອນ Wi-Fi ທີ່ຢູ່ໃກ້ຄຽງໄດ້"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"ຂໍ້ມູນບໍລິການການຈ່າຍເງິນ NFC ທີ່ຕ້ອງການ"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"ອະນຸຍາດໃຫ້ແອັບຮັບຂໍ້ມູນບໍລິການການຈ່າຍເງິນ NFC ທີ່ຕ້ອງການໄດ້ ເຊັ່ນ: ການຊ່ວຍເຫຼືອແບບລົງທະບຽນ ແລະ ປາຍທາງເສັ້ນທາງ."</string> <string name="permlab_nfc" msgid="1904455246837674977">"ຄວບຄຸມ Near Field Communication"</string> @@ -2431,54 +2435,30 @@ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"ເປີດໃຊ້"</string> <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"ກັບຄືນ"</string> <string name="unarchival_session_app_label" msgid="6811856981546348205">"ລໍຖ້າດຳເນີນການ..."</string> - <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) --> - <skip /> - <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) --> - <skip /> + <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"ຕອນນີ້ SOS ດາວທຽມພ້ອມໃຫ້ບໍລິການແລ້ວ"</string> + <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"ທ່ານສາມາດສົ່ງຂໍ້ຄວາມໄປຫາບໍລິການສຸກເສີນໄດ້ໃນກໍລະນີທີ່ບໍ່ມີເຄືອຂ່າຍມືຖື ຫຼື Wi-Fi. ຕ້ອງຕັ້ງຄ່າ Google Messages ເປັນແອັບຮັບສົ່ງຂໍ້ຄວາມເລີ່ມຕົ້ນຂອງທ່ານ."</string> + <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"ບໍ່ຮອງຮັບ SOS ດາວທຽມ"</string> + <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"ອຸປະກອນນີ້ບໍ່ຮອງຮັບ SOS ດາວທຽມ"</string> + <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"ບໍ່ໄດ້ຕັ້ງຄ່າ SOS ດາວທຽມ"</string> + <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"ກະລຸນາກວດສອບໃຫ້ໝັ້ນໃຈວ່າທ່ານໄດ້ເຊື່ອມຕໍ່ອິນເຕີເນັດແລ້ວ ແລະ ລອງຕັ້ງຄ່າອີກຄັ້ງ"</string> + <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"SOS ດາວທຽມບໍ່ພ້ອມໃຫ້ບໍລິການ"</string> + <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"SOS ດາວທຽມບໍ່ພ້ອມໃຫ້ບໍລິການໃນປະເທດ ຫຼື ພາກພື້ນນີ້"</string> + <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"ບໍ່ໄດ້ຕັ້ງຄ່າ SOS ດາວທຽມ"</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">"SOS ດາວທຽມບໍ່ພ້ອມໃຫ້ບໍລິການ"</string> + <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"ເພື່ອກວດສອບວ່າ 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> + <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"ອຸປະກອນນີ້ບໍ່ຮອງຮັບການຮັບສົ່ງຂໍ້ຄວາມຜ່ານດາວທຽມ"</string> + <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"ບໍ່ໄດ້ຕັ້ງຄ່າການຮັບສົ່ງຂໍ້ຄວາມຜ່ານດາວທຽມ"</string> + <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"ກະລຸນາກວດສອບໃຫ້ໝັ້ນໃຈວ່າທ່ານໄດ້ເຊື່ອມຕໍ່ອິນເຕີເນັດແລ້ວ ແລະ ລອງຕັ້ງຄ່າອີກຄັ້ງ"</string> + <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_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> <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-lt/strings.xml b/core/res/res/values-lt/strings.xml index c88b6635fab1..21fddd010ca4 100644 --- a/core/res/res/values-lt/strings.xml +++ b/core/res/res/values-lt/strings.xml @@ -614,6 +614,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Leisti programai nustatyti apytikslę netoliese esančių itin plataus dažnio juostos įrenginių poziciją"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"sąveikauti su „Wi‑Fi“ įrenginiais netoliese"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Leidžiama programai reklamuoti, prisijungti ir nustatyti apytikslę netoliese esančių „Wi-Fi“ įrenginių poziciją"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Pageidaujama ARL mokėjimo paslaugos informacija"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Programai leidžiama gauti pageidaujamą ARL mokamos paslaugos informaciją, pvz., užregistruotą pagalbą ir maršrutų tikslus."</string> <string name="permlab_nfc" msgid="1904455246837674977">"valdyti artimo lauko perdavimą (angl. „Near Field Communication“)"</string> @@ -2433,54 +2437,30 @@ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Įjungti"</string> <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Grįžti"</string> <string name="unarchival_session_app_label" msgid="6811856981546348205">"Laukiama..."</string> - <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) --> - <skip /> - <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) --> - <skip /> + <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"Prisijungimas prie palydovo kritiniu atveju nepasiekiamas"</string> + <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"Galite siųsti pranešimus pagalbos tarnyboms, kai nėra mobiliojo ryšio ar „Wi-Fi“ tinklo. „Google Messages“ turi būti jūsų numatytoji pranešimų programa."</string> + <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"Prisijungimo prie palydovo kritiniu atveju funkcija nepalaikoma"</string> + <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"Šiame įrenginyje nepalaikoma prisijungimo prie palydovo kritiniu atveju funkcija"</string> + <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"Prisijungimo prie palydovo kritiniu atveju funkcija nenustatyta"</string> + <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"Įsitikinkite, kad esate prisijungę prie interneto ir bandykite nustatyti dar kartą"</string> + <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"Prisijungimo prie palydovo kritiniu atveju funkcija nepasiekiama"</string> + <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"Prisijungimo prie palydovo kritiniu atveju funkcija nepasiekiama šioje šalyje arba regione"</string> + <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"Prisijungimo prie palydovo kritiniu atveju funkcija nenustatyta"</string> + <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"Jei norite siųsti pranešimus per palydovą, nustatykite „Google Messages“ kaip numatytąją pranešimų programą"</string> + <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"Prisijungimo prie palydovo kritiniu atveju funkcija nepasiekiama"</string> + <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"Jei norite patikrinti, ar prisijungimo prie palydovo kritiniu atveju funkcija pasiekiama šioje šalyje ar regione, įjunkite vietovės nustatymus"</string> + <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"Pasiekiama susirašinėjimo palydoviniais pranešimais paslauga"</string> + <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"Galite siųsti pranešimus palydoviniu ryšiu, kai nėra mobiliojo ryšio ar „Wi-Fi“ tinklo. „Google Messages“ turi būti jūsų numatytoji pranešimų programa."</string> + <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"Susirašinėjimo palydoviniais pranešimais paslauga nepalaikoma"</string> + <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"Šiame įrenginyje nepalaikoma susirašinėjimo palydoviniais pranešimais paslauga"</string> + <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"Susirašinėjimo palydoviniais pranešimais paslauga nenustatyta"</string> + <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"Įsitikinkite, kad esate prisijungę prie interneto ir bandykite nustatyti dar kartą"</string> + <string name="satellite_messaging_not_in_allowed_region_notification_title" msgid="2035303593479031655">"Susirašinėjimo palydoviniais pranešimais paslauga nepasiekiama"</string> + <string name="satellite_messaging_not_in_allowed_region_notification_summary" msgid="5270294879531815854">"Susirašinėjimo palydoviniais pranešimais paslauga nepasiekiama šioje šalyje ar regione"</string> + <string name="satellite_messaging_unsupported_default_sms_app_notification_title" msgid="1004808759472360189">"Susirašinėjimo palydoviniais pranešimais paslauga nenustatyta"</string> + <string name="satellite_messaging_unsupported_default_sms_app_notification_summary" msgid="17084124893763593">"Jei norite siųsti pranešimus per palydovą, nustatykite „Google Messages“ kaip numatytąją pranešimų programą"</string> + <string name="satellite_messaging_location_disabled_notification_title" msgid="7270641894250928494">"Susirašinėjimo palydoviniais pranešimais paslauga nepasiekiama"</string> + <string name="satellite_messaging_location_disabled_notification_summary" msgid="1450824950686221810">"Jei norite patikrinti, ar susirašinėjimo palydoviniais pranešimais paslauga pasiekiama šioje šalyje ar regione, įjunkite vietovės nustatymus"</string> <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Atrakinimo piršto atspaudu nustatymas dar kartą"</string> <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> nebegalima atpažinti."</string> <string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> ir <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> nebegalima atpažinti."</string> diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml index 38d1891b13e2..15ee04a5f1d7 100644 --- a/core/res/res/values-lv/strings.xml +++ b/core/res/res/values-lv/strings.xml @@ -613,6 +613,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Atļaut lietotnei noteikt relatīvo atrašanās vietu starp tuvumā esošām ultraplatjoslas ierīcēm"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"Mijiedarbība ar tuvumā esošām Wi‑Fi ierīcēm"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Atļauj lietotnei nodot datus tuvumā esošām Wi‑Fi ierīcē, izveidot savienojumu ar tām un noteikt to relatīvo pozīciju."</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informācija par vēlamo NFC maksājumu pakalpojumu"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Ļauj lietotnei iegūt informāciju par vēlamo NFC maksājumu pakalpojumu, piemēram, par reģistrētajiem lietojumprogrammu ID un maršruta galamērķi."</string> <string name="permlab_nfc" msgid="1904455246837674977">"kontrolē tuvlauka saziņu"</string> diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml index 87fd79928298..b5187726028b 100644 --- a/core/res/res/values-mk/strings.xml +++ b/core/res/res/values-mk/strings.xml @@ -612,6 +612,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Дозволува апликацијата да ја одреди релативната положба помеѓу уредите со ултраширок појас во близина"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"да има интеракција со уредите со Wi‑Fi во близина"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Дозволува апликацијата да рекламира, да се поврзува и да ја одредува релативната положба на уреди со Wi‑Fi во близина"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Информации за претпочитаната услуга за плаќање преку NFC"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Дозволува апликацијата да добие информации за претпочитаната услуга за плаќање преку NFC, како регистрирани помагала и дестинација на маршрутата."</string> <string name="permlab_nfc" msgid="1904455246837674977">"контролирај комуникација на блиско поле"</string> diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml index 086ebe1fcaa5..aabeb9b98646 100644 --- a/core/res/res/values-ml/strings.xml +++ b/core/res/res/values-ml/strings.xml @@ -612,6 +612,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"സമീപമുള്ള അൾട്രാ-വെെഡ്ബാൻഡ് ഉപകരണങ്ങൾ തമ്മിലുള്ള ആപേക്ഷിക സ്ഥാനം നിർണ്ണയിക്കാൻ ആപ്പിനെ അനുവദിക്കുക"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"സമീപമുള്ള വൈഫൈ ഉപകരണങ്ങളുമായി ഇടപഴകുക"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"സമീപമുള്ള വൈഫൈ ഉപകരണങ്ങൾ കാണിക്കാനും അവയിലേക്ക് കണക്റ്റ് ചെയ്യാനും അവയുടെ ആപേക്ഷിക സ്ഥാനം നിർണ്ണയിക്കാനും ആപ്പിനെ അനുവദിക്കൂ"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"തിരഞ്ഞെടുത്ത NFC പേയ്മെന്റ് സേവനത്തെ സംബന്ധിച്ച വിവരങ്ങൾ"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"റൂട്ട് ലക്ഷ്യസ്ഥാനം, രജിസ്റ്റർ ചെയ്തിരിക്കുന്ന സഹായങ്ങൾ എന്നിവ പോലുള്ള, തിരഞ്ഞെടുത്ത NFC പേയ്മെന്റ് സേവനത്തെ സംബന്ധിച്ച വിവരങ്ങൾ ലഭിക്കാൻ ആപ്പിനെ അനുവദിക്കുന്നു."</string> <string name="permlab_nfc" msgid="1904455246837674977">"സമീപ ഫീൽഡുമായുള്ള ആശയവിനിമയം നിയന്ത്രിക്കുക"</string> @@ -2431,54 +2435,30 @@ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"ഓണാക്കുക"</string> <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"മടങ്ങുക"</string> <string name="unarchival_session_app_label" msgid="6811856981546348205">"തീർപ്പാക്കിയിട്ടില്ല..."</string> - <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) --> - <skip /> - <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) --> - <skip /> + <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"സാറ്റലൈറ്റ് SOS ഇപ്പോൾ ലഭ്യമാണ്"</string> + <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"മൊബൈൽ അല്ലെങ്കിൽ വൈഫൈ നെറ്റ്വർക്ക് ലഭ്യമല്ലെങ്കിൽ നിങ്ങൾക്ക് അടിയന്തര സേവനങ്ങൾക്ക് സന്ദേശമയയ്ക്കാം. നിങ്ങളുടെ ഡിഫോൾട്ട് സന്ദേശമയയ്ക്കൽ ആപ്പ് Google Messages ആയിരിക്കണം."</string> + <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"സാറ്റലൈറ്റ് SOS പിന്തുണയ്ക്കുന്നില്ല"</string> + <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"ഈ ഉപകരണത്തിൽ സാറ്റലൈറ്റ് SOS പിന്തുണയ്ക്കുന്നില്ല"</string> + <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"സാറ്റലൈറ്റ് SOS സജ്ജീകരിച്ചിട്ടില്ല"</string> + <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"നിങ്ങൾ ഇന്റർനെറ്റിലേക്ക് കണക്റ്റ് ചെയ്തിട്ടുണ്ടെന്ന് ഉറപ്പാക്കിയ ശേഷം സജ്ജീകരിക്കാൻ വീണ്ടും ശ്രമിക്കുക"</string> + <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"സാറ്റലൈറ്റ് SOS ലഭ്യമല്ല"</string> + <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"ഈ രാജ്യത്ത് അല്ലെങ്കിൽ പ്രദേശത്ത് സാറ്റലൈറ്റ് SOS ലഭ്യമല്ല"</string> + <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"സാറ്റലൈറ്റ് SOS സജ്ജീകരിച്ചിട്ടില്ല"</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">"സാറ്റലൈറ്റ് SOS ലഭ്യമല്ല"</string> + <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"ഈ രാജ്യത്ത് അല്ലെങ്കിൽ പ്രദേശത്ത് സാറ്റലൈറ്റ് SOS ലഭ്യമാണോയെന്ന് പരിശോധിക്കുന്നതിന്, ലൊക്കേഷൻ ക്രമീകരണം ഓണാക്കുക"</string> + <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"സാറ്റലൈറ്റ് സഹായത്തോടെ സന്ദേശമയയ്ക്കൽ ലഭ്യമാണ്"</string> + <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"മൊബൈൽ അല്ലെങ്കിൽ വൈഫൈ നെറ്റ്വർക്ക് ഇല്ലെങ്കിൽ നിങ്ങൾക്ക് സാറ്റലൈറ്റ് വഴി സന്ദേശമയയ്ക്കാം. നിങ്ങളുടെ ഡിഫോൾട്ട് സന്ദേശമയയ്ക്കൽ ആപ്പ് Google Messages ആയിരിക്കണം."</string> + <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"സാറ്റലൈറ്റ് സഹായത്തോടെ സന്ദേശമയയ്ക്കൽ പിന്തുണയ്ക്കുന്നില്ല"</string> + <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"ഈ ഉപകരണത്തിൽ സാറ്റലൈറ്റ് സഹായത്തോടെ സന്ദേശമയയ്ക്കൽ പിന്തുണയ്ക്കുന്നില്ല"</string> + <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"സാറ്റലൈറ്റ് സഹായത്തോടെ സന്ദേശമയയ്ക്കൽ സജ്ജീകരിച്ചിട്ടില്ല"</string> + <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"നിങ്ങൾ ഇന്റർനെറ്റിലേക്ക് കണക്റ്റ് ചെയ്തിട്ടുണ്ടെന്ന് ഉറപ്പാക്കിയ ശേഷം സജ്ജീകരിക്കാൻ വീണ്ടും ശ്രമിക്കുക"</string> + <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_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> <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> @@ -2498,7 +2478,7 @@ <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"SMS"</string> <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"സംഗീതം"</string> <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"കലണ്ടർ"</string> - <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"കാൽക്കുലേറ്റർ"</string> + <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Calculator"</string> <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Maps"</string> <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"ആപ്പുകൾ"</string> <string name="fingerprint_loe_notification_msg" msgid="3927447270148854546">"നിങ്ങളുടെ ഫിംഗർപ്രിന്റുകൾ ഇനി തിരിച്ചറിയാനാകില്ല. ഫിംഗർപ്രിന്റ് അൺലോക്ക് വീണ്ടും സജ്ജീകരിക്കുക."</string> diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml index 42db4c97ace4..c625d31dbd79 100644 --- a/core/res/res/values-mn/strings.xml +++ b/core/res/res/values-mn/strings.xml @@ -612,6 +612,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Аппад ойролцоох ультра өргөн зурвасын төхөөрөмжүүдийн хоорондох холбоотой байрлалыг тодорхойлохыг зөвшөөрөх"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"ойролцоох Wi-Fi төхөөрөмжүүдтэй харилцах"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Аппад ойролцоох Wi-Fi төхөөрөмжүүдтэй холбоотой байрлалыг мэдэгдэх, холбох, тодорхойлохыг зөвшөөрнө"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Сонгосон NFC төлбөрийн үйлчилгээний мэдээлэл"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Бүртгүүлсэн төхөөрөмж болон маршрутын хүрэх цэг зэрэг сонгосон nfc төлбөрийн үйлчилгээний мэдээллийг авахыг аппад зөвшөөрдөг."</string> <string name="permlab_nfc" msgid="1904455246837674977">"ойролцоо талбарын холбоог удирдах"</string> diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml index 2cfeaa2adab9..3240d5914056 100644 --- a/core/res/res/values-mr/strings.xml +++ b/core/res/res/values-mr/strings.xml @@ -612,6 +612,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"ॲपला जवळच्या अल्ट्रा-वाइडबँड डिव्हाइसदरम्यानचे संबंधित स्थान निर्धारित करण्याची अनुमती द्या"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"जवळपासच्या वाय-फाय डिव्हाइसशी संवाद साधा"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"ॲपला जाहिरात करण्याची, कनेक्ट करण्याची आणि जवळपासच्या वाय-फाय डिव्हाइसचे संबंधित स्थान निर्धारित करण्याची परवानगी देते"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"प्राधान्यकृत NFC पेमेंट सेवा माहिती"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"नोंदणीकृत एड्स आणि मार्ग गंतव्यस्थान सारखी प्राधान्यकृत एनएफसी पेमेंट सेवेची माहिती मिळवण्यासाठी अॅपला अनुमती देते."</string> <string name="permlab_nfc" msgid="1904455246837674977">"फील्ड जवळील कम्युनिकेशन नियंत्रित करा"</string> @@ -2431,54 +2435,30 @@ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"सुरू करा"</string> <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"मागे जा"</string> <string name="unarchival_session_app_label" msgid="6811856981546348205">"प्रलंबित आहे..."</string> - <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) --> - <skip /> - <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) --> - <skip /> + <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"सॅटेलाइट SOS आता उपलब्ध आहे"</string> + <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"कोणतेही मोबाइल किंवा वाय-फाय नेटवर्क उपलब्ध नसल्यास, तुम्ही आणीबाणी सेवांना मेसेज पाठवू शकता. Google Messages हे तुमचे डीफॉल्ट मेसेजिंग ॲप असणे आवश्यक आहे."</string> + <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"सॅटेलाइट SOS ला सपोर्ट नाही"</string> + <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"या डिव्हाइसवर सॅटेलाइट SOS ला सपोर्ट नाही"</string> + <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"सॅटेलाइट SOS सेट केलेले नाही"</string> + <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"तुम्ही इंटरनेटशी कनेक्ट केले असल्याची खात्री करा आणि पुन्हा सेटअप करून पहा"</string> + <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"सॅटेलाइट SOS उपलब्ध नाही"</string> + <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"या देशात किंवा प्रदेशात सॅटेलाइट SOS उपलब्ध नाही"</string> + <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"सॅटेलाइट SOS सेट केलेले नाही"</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">"सॅटेलाइट SOS उपलब्ध नाही"</string> + <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"या देशात किंवा प्रदेशात सॅटेलाइट SOS उपलब्ध आहे का हे तपासण्यासाठी, स्थान सेटिंग्ज सुरू करा"</string> + <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"सॅटेलाइट मेसेजिंग उपलब्ध आहे"</string> + <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"मोबाईल किंवा वाय-फाय नेटवर्क नसल्यास, तुम्ही सॅटेलाइटद्वारे मेसेज पाठवू शकता. Google Messages हे तुमचे डीफॉल्ट मेसेजिंग ॲप असणे आवश्यक आहे."</string> + <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"सॅटेलाइट मेसेजिंगला सपोर्ट नाही"</string> + <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"या डिव्हाइसवर सॅटेलाइट मेसेजिंगला सपोर्ट नाही"</string> + <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"सॅटेलाइट मेसेजिंग सेट केलेले नाही"</string> + <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"तुम्ही इंटरनेटशी कनेक्ट केले असल्याची खात्री करा आणि पुन्हा सेटअप करून पहा"</string> + <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_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> <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-ms/strings.xml b/core/res/res/values-ms/strings.xml index 6f5d5e7c3658..bd780ae0be92 100644 --- a/core/res/res/values-ms/strings.xml +++ b/core/res/res/values-ms/strings.xml @@ -612,6 +612,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Benarkan apl menentukan kedudukan relatif antara peranti Ultrajalur Lebar berdekatan"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"berinteraksi dengan peranti Wi-Fi berdekatan"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Membenarkan apl mengiklankan, menyambung dan menentukan kedudukan relatif peranti Wi-Fi berdekatan"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Maklumat Perkhidmatan Pembayaran NFC Pilihan"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Membenarkan apl mendapatkan maklumat perkhidmatan pembayaran nfc pilihan seperti bantuan berdaftar dan destinasi laluan."</string> <string name="permlab_nfc" msgid="1904455246837674977">"mengawal Komunikasi Medan Dekat"</string> @@ -2431,54 +2435,30 @@ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Hidupkan"</string> <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Kembali"</string> <string name="unarchival_session_app_label" msgid="6811856981546348205">"Belum selesai..."</string> - <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) --> - <skip /> - <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) --> - <skip /> + <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"SOS via Satelit kini tersedia"</string> + <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"Anda boleh menghantar mesej kepada perkhidmatan kecemasan jika tiada rangkaian mudah alih atau Wi-Fi. Google Messages mestilah ditetapkan sebagai apl pemesejan lalai anda."</string> + <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"SOS via Satelit tidak disokong"</string> + <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"SOS via Satelit tidak disokong pada peranti ini"</string> + <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"SOS via Satelit tidak disediakan"</string> + <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"Pastikan anda disambungkan kepada Internet dan cuba buat persediaan sekali lagi"</string> + <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"SOS via Satelit tidak tersedia"</string> + <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"SOS via Satelit tidak tersedia di negara atau rantau ini"</string> + <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"SOS via Satelit tidak disediakan"</string> + <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"Untuk menghantar mesej melalui satelit, tetapkan Google Messages sebagai apl pemesejan lalai anda"</string> + <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"SOS via Satelit tidak tersedia"</string> + <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"Untuk menyemak sama ada SOS via Satelit tersedia di negara atau rantau ini, hidupkan tetapan lokasi"</string> + <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"Permesejan satelit tersedia"</string> + <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"Anda boleh menghantar mesej melalui satelit jika tiada rangkaian mudah alih atau Wi-Fi. Google Messages mestilah ditetapkan sebagai apl pemesejan lalai anda."</string> + <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"Permesejan satelit tidak disokong"</string> + <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"Permesejan satelit tidak disokong pada peranti ini"</string> + <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"Permesejan satelit tidak disediakan"</string> + <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"Pastikan anda disambungkan kepada Internet dan cuba buat persediaan sekali lagi"</string> + <string name="satellite_messaging_not_in_allowed_region_notification_title" msgid="2035303593479031655">"Permesejan satelit tidak tersedia"</string> + <string name="satellite_messaging_not_in_allowed_region_notification_summary" msgid="5270294879531815854">"Permesejan satelit tidak tersedia di negara atau rantau ini"</string> + <string name="satellite_messaging_unsupported_default_sms_app_notification_title" msgid="1004808759472360189">"Permesejan satelit tidak disediakan"</string> + <string name="satellite_messaging_unsupported_default_sms_app_notification_summary" msgid="17084124893763593">"Untuk menghantar mesej melalui satelit, tetapkan Google Messages sebagai apl pemesejan lalai anda"</string> + <string name="satellite_messaging_location_disabled_notification_title" msgid="7270641894250928494">"Permesejan satelit tidak tersedia"</string> + <string name="satellite_messaging_location_disabled_notification_summary" msgid="1450824950686221810">"Untuk menyemak sama ada permesejan satelit tersedia di negara atau rantau ini, hidupkan tetapan lokasi"</string> <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Sediakan Buka Kunci Cap Jari sekali lagi"</string> <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> tidak dapat dicam lagi."</string> <string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> dan <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> tidak dapat dicam lagi."</string> diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml index 9e7e78f98c64..e4c2acaba9f5 100644 --- a/core/res/res/values-my/strings.xml +++ b/core/res/res/values-my/strings.xml @@ -612,6 +612,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"အနီးရှိ ‘အလွန်ကျယ်ပြန့်သော လှိုင်းအလျားသုံးစက်များ’ ကြား မှန်းခြေနေရာကို သတ်မှတ်ရန် အက်ပ်ကို ခွင့်ပြုမည်"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"အနီးရှိ Wi-Fi စက်များနှင့် ပြန်လှန်တုံ့ပြန်ခြင်း"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"ကြော်ငြာရန်၊ ချိတ်ဆက်ရန်နှင့် အနီးတစ်ဝိုက်ရှိ Wi-Fi စက်များ၏ နေရာကို သတ်မှတ်ရန် အက်ပ်ကို ခွင့်ပြုသည်"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"ဦးစားပေး NFC ငွေပေးချေမှုဆိုင်ရာ ဝန်ဆောင်မှု အချက်အလက်များ"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"အက်ပ်အား ဦစားပေး NFC ငွေပေးချေမှုဆိုင်ရာ ဝန်ဆောင်မှု အချက်အလက်များဖြစ်သည့် မှတ်ပုံတင်ထားသော အကူအညီများနှင့် သွားလာရာ လမ်းကြောင်းတို့ကို ရယူရန် ခွင့်ပြုသည်။"</string> <string name="permlab_nfc" msgid="1904455246837674977">"Near Field Communicationအား ထိန်းချုပ်ရန်"</string> @@ -2431,54 +2435,30 @@ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"ဖွင့်ရန်"</string> <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"နောက်သို့"</string> <string name="unarchival_session_app_label" msgid="6811856981546348205">"ဆိုင်းငံ့ထားသည်…"</string> - <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) --> - <skip /> - <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) --> - <skip /> + <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"Satellite SOS ကို ယခုသုံးနိုင်ပြီ"</string> + <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"မိုဘိုင်း (သို့) Wi-Fi ကွန်ရက် မရှိပါက အရေးပေါ်ဝန်ဆောင်မှု ဌာနများသို့ မက်ဆေ့ဂျ်ပို့နိုင်သည်။ Google Messages သည် သင်၏ မူရင်းမက်ဆေ့ဂျ်ပို့ရန်အက်ပ် ဖြစ်ရမည်။"</string> + <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"Satellite SOS ကို ပံ့ပိုးမထားပါ"</string> + <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"Satellite SOS ကို ဤစက်တွင် ပံ့ပိုးမထားပါ"</string> + <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"Satellite SOS ကို စနစ်ထည့်သွင်းမထားပါ"</string> + <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"အင်တာနက်နှင့် ချိတ်ဆက်ထားခြင်း ရှိ၊ မရှိ စစ်ဆေးပြီး စနစ်ထပ်မံထည့်သွင်းကြည့်ပါ"</string> + <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_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_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> + <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"ဂြိုဟ်တုမှတစ်ဆင့် မက်ဆေ့ဂျ်ပို့ခြင်းကို ဤစက်တွင် ပံ့ပိုးမထားပါ"</string> + <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"ဂြိုဟ်တုမှတစ်ဆင့် မက်ဆေ့ဂျ်ပို့ခြင်းကို စနစ်ထည့်သွင်းမထားပါ"</string> + <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"အင်တာနက်နှင့် ချိတ်ဆက်ထားခြင်း ရှိ၊ မရှိ စစ်ဆေးပြီး စနစ်ထပ်မံထည့်သွင်းကြည့်ပါ"</string> + <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_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> <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-nb/strings.xml b/core/res/res/values-nb/strings.xml index 683ca2661b6b..ec4cd961faee 100644 --- a/core/res/res/values-nb/strings.xml +++ b/core/res/res/values-nb/strings.xml @@ -612,6 +612,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"tillate at appen fastslår den relative posisjonen mellom enheter i nærheten som bruker ultrabredbånd"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"samhandle med wifi-enheter i nærheten"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Lar appen annonsere, koble til og fastslå den relative posisjonen til wifi-enheter i nærheten"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informasjon om prioritert NFC-betalingstjeneste"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Gir appen tilgang til informasjon om prioritert NFC-betalingstjeneste, for eksempel registrerte hjelpemidler og destinasjon."</string> <string name="permlab_nfc" msgid="1904455246837674977">"kontroller overføring av data med NFC-teknologi"</string> diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml index ffeecb1cf0ba..0dc0bd3d04a9 100644 --- a/core/res/res/values-ne/strings.xml +++ b/core/res/res/values-ne/strings.xml @@ -293,7 +293,7 @@ <string name="notification_channel_account" msgid="6436294521740148173">"खाताको स्थिति"</string> <string name="notification_channel_developer" msgid="1691059964407549150">"विकासकर्ताका म्यासेजहरू"</string> <string name="notification_channel_developer_important" msgid="7197281908918789589">"विकासकर्तासम्बन्धी महत्त्वपूर्ण म्यासेजहरू"</string> - <string name="notification_channel_updates" msgid="7907863984825495278">"अद्यावधिकहरू"</string> + <string name="notification_channel_updates" msgid="7907863984825495278">"अपडेटहरू"</string> <string name="notification_channel_network_status" msgid="2127687368725272809">"नेटवर्कको स्थिति"</string> <string name="notification_channel_network_alerts" msgid="6312366315654526528">"नेटवर्कका अलर्टहरू"</string> <string name="notification_channel_network_available" msgid="6083697929214165169">"नेटवर्क उपलब्ध छ"</string> @@ -612,6 +612,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"यो एपलाई नजिकै रहेका अल्ट्रा-वाइडब्यान्ड चल्ने डिभाइसहरूबिचको तुलनात्मक स्थान पत्ता लगाउन दिनुहोस्"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"Wi-Fi चल्ने नजिकै रहेका डिभाइसहरूमा चलाउन दिन्छ"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"यसले एपलाई Wi-Fi चल्ने नजिकै रहेका डिभाइसहरूमा विज्ञापन गर्न, कनेक्ट गर्न र सापेक्ष स्थिति निर्धारण गर्न दिन्छ"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"NFC भुक्तानी सेवासम्बन्धी रुचाइएको जानकारी"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"यसले एपलाई दर्ता गरिएका सहायता तथा मार्गको गन्तव्य जस्ता रुचाइएका NFC भुक्तानी सेवासम्बन्धी जानकारी प्राप्त गर्न दिन्छ।"</string> <string name="permlab_nfc" msgid="1904455246837674977">"नजिक क्षेत्र संचार नियन्त्रणहरू"</string> @@ -2431,54 +2435,30 @@ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"अन गर्नुहोस्"</string> <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"पछाडि जानुहोस्"</string> <string name="unarchival_session_app_label" msgid="6811856981546348205">"विचाराधीन..."</string> - <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) --> - <skip /> - <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) --> - <skip /> + <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"स्याटलाइट SOS अब उपलब्ध भएको छ"</string> + <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"मोबाइल वा Wi-Fi नेटवर्क नभएका खण्डमा तपाईं आपत्कालीन सेवामा म्यासेज पठाउन सक्नुहुन्छ। Google Messages अनिवार्य रूपमा तपाईंको डिफल्ट म्यासेजिङ एप हुनु पर्छ।"</string> + <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"स्याटलाइट SOS प्रयोग गर्न मिल्दैन"</string> + <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"यो डिभाइसमा स्याटलाइट SOS प्रयोग गर्न मिल्दैन"</string> + <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"स्याटलाइट SOS सेटअप गरिएको छैन"</string> + <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"तपाईंको डिभाइस इन्टरनेटमा कनेक्ट गरिएको छ भन्ने कुरा सुनिश्चित गर्नुहोस् र फेरि सेटअप गरी हेर्नुहोस्"</string> + <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"स्याटलाइट SOS उपलब्ध छैन"</string> + <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"यो देश वा क्षेत्रमा स्याटलाइट SOS उपलब्ध छैन"</string> + <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"स्याटलाइट SOS सेटअप गरिएको छैन"</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">"स्याटलाइट SOS उपलब्ध छैन"</string> + <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"यो देश वा क्षेत्रमा स्याटलाइट 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> + <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"यो डिभाइसमा स्याटलाइटमार्फत म्यासेज पठाउने सुविधा प्रयोग गर्न मिल्दैन"</string> + <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"स्याटलाइटमार्फत म्यासेज पठाउने सुविधा सेटअप गरिएको छैन"</string> + <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"तपाईंको डिभाइस इन्टरनेटमा कनेक्ट गरिएको छ भन्ने कुरा सुनिश्चित गर्नुहोस् र फेरि सेटअप गरी हेर्नुहोस्"</string> + <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_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> <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-nl/strings.xml b/core/res/res/values-nl/strings.xml index 5f77e7304cdb..e55334740778 100644 --- a/core/res/res/values-nl/strings.xml +++ b/core/res/res/values-nl/strings.xml @@ -612,6 +612,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"De app toestaan om de relatieve positie tussen ultrabreedbandapparaten in de buurt te bepalen"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"interactie met wifi-apparaten in de buurt"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Hiermee kan de app uitzenden, verbindingen maken en de relatieve positie bepalen van wifi-apparaten in de buurt"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informatie over voorkeursservice voor NFC-betaling"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Hiermee kun je zorgen dat de app informatie krijgt over de voorkeursservice voor NFC-betaling, zoals geregistreerde hulpmiddelen en routebestemmingen."</string> <string name="permlab_nfc" msgid="1904455246837674977">"Near Field Communication regelen"</string> @@ -2431,54 +2435,30 @@ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Aanzetten"</string> <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Terug"</string> <string name="unarchival_session_app_label" msgid="6811856981546348205">"In behandeling…"</string> - <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) --> - <skip /> - <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) --> - <skip /> + <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"SOS via satelliet is nu beschikbaar"</string> + <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"Je kunt berichten sturen naar de hulpdiensten als er geen mobiel of wifi-netwerk beschikbaar is. Google Berichten moet je standaard berichten-app zijn."</string> + <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"SOS via satelliet wordt niet ondersteund"</string> + <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"SOS via satelliet wordt niet ondersteund op dit apparaat"</string> + <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"SOS via satelliet is niet ingesteld"</string> + <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"Zorg dat je verbinding hebt met internet en probeer het opnieuw"</string> + <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"SOS via satelliet is niet beschikbaar"</string> + <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"SOS via satelliet is niet beschikbaar in dit land of deze regio"</string> + <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"SOS via satelliet niet ingesteld"</string> + <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"Stel Google Berichten in als je standaard berichten-app om via satelliet berichten te sturen"</string> + <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"SOS via satelliet is niet beschikbaar"</string> + <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"Zet locatie-instellingen aan om te checken of SOS via satelliet beschikbaar is in dit land of deze regio"</string> + <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"Satellietberichten beschikbaar"</string> + <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"Je kunt via satelliet berichten sturen als er geen mobiel of wifi-netwerk beschikbaar is. Google Berichten moet je standaard berichten-app zijn."</string> + <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"Satellietberichten niet ondersteund"</string> + <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"Satellietberichten worden niet ondersteund op dit apparaat"</string> + <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"Satellietberichten niet ingesteld"</string> + <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"Zorg dat je verbinding hebt met internet en probeer het opnieuw"</string> + <string name="satellite_messaging_not_in_allowed_region_notification_title" msgid="2035303593479031655">"Satellietberichten niet beschikbaar"</string> + <string name="satellite_messaging_not_in_allowed_region_notification_summary" msgid="5270294879531815854">"Satellietberichten zijn niet beschikbaar in dit land of deze regio"</string> + <string name="satellite_messaging_unsupported_default_sms_app_notification_title" msgid="1004808759472360189">"Satellietberichten niet ingesteld"</string> + <string name="satellite_messaging_unsupported_default_sms_app_notification_summary" msgid="17084124893763593">"Stel Google Berichten in als je standaard berichten-app om via satelliet berichten te sturen"</string> + <string name="satellite_messaging_location_disabled_notification_title" msgid="7270641894250928494">"Satellietberichten niet beschikbaar"</string> + <string name="satellite_messaging_location_disabled_notification_summary" msgid="1450824950686221810">"Zet locatie-instellingen aan om te checken of satellietberichten beschikbaar zijn in dit land of deze regio"</string> <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Ontgrendelen met vingerafdruk weer instellen"</string> <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> wordt niet meer herkend."</string> <string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> en <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> worden niet meer herkend."</string> diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml index b4f924b7d9db..2515bb5052eb 100644 --- a/core/res/res/values-or/strings.xml +++ b/core/res/res/values-or/strings.xml @@ -612,6 +612,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"ଆଖପାଖର ଅଲଟ୍ରା-ୱାଇଡବ୍ୟାଣ୍ଡ ଡିଭାଇସଗୁଡ଼ିକ ମଧ୍ୟରେ ଆପେକ୍ଷିକ ଅବସ୍ଥିତିକୁ ନିର୍ଦ୍ଧାରଣ କରିବା ପାଇଁ ଆପକୁ ଅନୁମତି ଦିଅନ୍ତୁ"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"ଆଖପାଖର ୱାଇ-ଫାଇ ଡିଭାଇସଗୁଡ଼ିକ ସହ ଇଣ୍ଟରାକ୍ଟ କରନ୍ତୁ"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"ଆଖପାଖର ୱାଇ-ଫାଇ ଡିଭାଇସରେ ବିଜ୍ଞାପନ ଦେବା, ତା ସହ ସଂଯୋଗ କରିବା ଓ ତା’ର ଆପେକ୍ଷିକ ଅବସ୍ଥିତି ନିର୍ଦ୍ଧାରଣ କରିବା ପାଇଁ ଆପକୁ ଅନୁମତି ଦିଏ"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"ପସନ୍ଦର NFC ପେମେଣ୍ଟ ସେବା ସୂଚନା"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"ପଞ୍ଜିକୃତ ଯନ୍ତ୍ର ଏବଂ ମାର୍ଗ ଲକ୍ଷସ୍ଥଳ ପରି ପସନ୍ଦର nfc ପେମେଣ୍ଟ ସେବା ସୂଚନା ପାଇବାକୁ ଆପ୍ ଅନୁମତି କରିଥାଏ।"</string> <string name="permlab_nfc" msgid="1904455246837674977">"ନିଅର୍ ଫିଲ୍ଡ କମ୍ୟୁନିକେଶନ୍ ଉପରେ ନିୟନ୍ତ୍ରଣ ରଖନ୍ତୁ"</string> diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml index fae806c4deca..574d991b9dfc 100644 --- a/core/res/res/values-pa/strings.xml +++ b/core/res/res/values-pa/strings.xml @@ -612,6 +612,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"ਐਪ ਨੂੰ ਨਜ਼ਦੀਕੀ ਅਲਟ੍ਰਾ-ਵਾਈਡਬੈਂਡ ਡੀਵਾਈਸਾਂ ਦੇ ਵਿਚਾਲੇ ਸੰਬੰਧਿਤ ਸਥਿਤੀ ਨੂੰ ਨਿਰਧਾਰਿਤ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਦਿਓ"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"ਨਜ਼ਦੀਕੀ ਵਾਈ-ਫਾਈ ਡੀਵਾਈਸਾਂ ਨਾਲ ਅੰਤਰਕਿਰਿਆ ਕਰੋ"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"ਐਪ ਨੂੰ ਨਜ਼ਦੀਕੀ ਵਾਈ-ਫਾਈ ਡੀਵਾਈਸਾਂ \'ਤੇ ਵਿਗਿਆਪਨ ਦੇਣ, ਕਨੈਕਟ ਕਰਨ ਅਤੇ ਉਨ੍ਹਾਂ ਦੀ ਸੰਬੰਧਿਤ ਸਥਿਤੀ ਨੂੰ ਨਿਰਧਾਰਿਤ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੱਤੀ ਜਾਂਦੀ ਹੈ"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"ਤਰਜੀਹੀ NFC ਭੁਗਤਾਨਸ਼ੁਦਾ ਸੇਵਾ ਜਾਣਕਾਰੀ"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"ਐਪ ਨੂੰ ਤਰਜੀਹੀ NFC ਭੁਗਤਾਨਸ਼ੁਦਾ ਸੇਵਾ ਜਾਣਕਾਰੀ ਪ੍ਰਾਪਤ ਕਰਨ ਦਿੰਦਾ ਹੈ ਜਿਵੇਂ ਕਿ ਰਜਿਸਟਰ ਕੀਤੇ ਸਾਧਨ ਅਤੇ ਮੰਜ਼ਿਲ ਰਸਤਾ।"</string> <string name="permlab_nfc" msgid="1904455246837674977">"ਨਜ਼ਦੀਕੀ ਖੇਤਰ ਸੰਚਾਰ ਤੇ ਨਿਯੰਤਰਣ ਪਾਓ"</string> @@ -2431,54 +2435,30 @@ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"ਚਾਲੂ ਕਰੋ"</string> <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"ਵਾਪਸ ਜਾਓ"</string> <string name="unarchival_session_app_label" msgid="6811856981546348205">"ਵਿਚਾਰ-ਅਧੀਨ..."</string> - <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) --> - <skip /> - <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) --> - <skip /> + <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"ਸੈਟੇਲਾਈਟ SOS ਦੀ ਸੁਵਿਧਾ ਹੁਣ ਉਪਲਬਧ ਹੈ"</string> + <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"ਮੋਬਾਈਲ ਜਾਂ ਵਾਈ-ਫਾਈ ਨੈੱਟਵਰਕ ਨਾ ਹੋਣ \'ਤੇ, ਤੁਸੀਂ ਐਮਰਜੈਂਸੀ ਸੇਵਾਵਾਂ ਨੂੰ ਸੁਨੇਹਾ ਭੇਜ ਸਕਦੇ ਹੋ। Google Messages ਤੁਹਾਡੀ ਪੂਰਵ-ਨਿਰਧਾਰਿਤ ਸੁਨੇਹਾ ਐਪ ਹੋਣੀ ਲਾਜ਼ਮੀ ਹੈ।"</string> + <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"ਸੈਟੇਲਾਈਟ SOS ਦੀ ਸੁਵਿਧਾ ਸਮਰਥਿਤ ਨਹੀਂ ਹੈ"</string> + <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"ਸੈਟੇਲਾਈਟ SOS ਦੀ ਸੁਵਿਧਾ ਇਸ ਡੀਵਾਈਸ \'ਤੇ ਸਮਰਥਿਤ ਨਹੀਂ ਹੈ"</string> + <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"ਸੈਟੇਲਾਈਟ SOS ਦੀ ਸੁਵਿਧਾ ਦਾ ਸੈੱਟ ਅੱਪ ਨਹੀਂ ਕੀਤਾ ਗਿਆ ਹੈ"</string> + <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"ਪੱਕਾ ਕਰੋ ਕਿ ਤੁਸੀਂ ਇੰਟਰਨੈੱਟ ਨਾਲ ਕਨੈਕਟ ਹੋ ਅਤੇ ਦੁਬਾਰਾ ਸੈੱਟਅੱਪ ਕਰਨ ਦੀ ਕੋਸ਼ਿਸ਼ ਕਰੋ"</string> + <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"ਸੈਟੇਲਾਈਟ SOS ਦੀ ਸੁਵਿਧਾ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string> + <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"ਸੈਟੇਲਾਈਟ SOS ਦੀ ਸੁਵਿਧਾ ਇਸ ਦੇਸ਼ ਜਾਂ ਖੇਤਰ ਵਿੱਚ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string> + <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"ਸੈਟੇਲਾਈਟ SOS ਦੀ ਸੁਵਿਧਾ ਦਾ ਸੈੱਟ ਅੱਪ ਨਹੀਂ ਹੋਇਆ"</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">"ਸੈਟੇਲਾਈਟ SOS ਦੀ ਸੁਵਿਧਾ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string> + <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"ਇਸਦੀ ਜਾਂਚ ਕਰਨ ਲਈ ਕਿ ਇਸ ਦੇਸ਼ ਜਾਂ ਖੇਤਰ ਵਿੱਚ ਸੈਟੇਲਾਈਟ SOS ਦੀ ਸੁਵਿਧਾ ਉਪਲਬਧ ਹੈ, ਟਿਕਾਣਾ ਸੈਟਿੰਗਾਂ ਨੂੰ ਚਾਲੂ ਕਰੋ"</string> + <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"ਸੈਟੇਲਾਈਟ ਸੁਨੇਹੇ ਦੀ ਸੁਵਿਧਾ ਉਪਲਬਧ ਹੈ"</string> + <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"ਮੋਬਾਈਲ ਜਾਂ ਵਾਈ-ਫਾਈ ਨੈੱਟਵਰਕ ਨਾ ਹੋਣ \'ਤੇ, ਤੁਸੀਂ ਸੈਟੇਲਾਈਟ ਰਾਹੀਂ ਸੁਨੇਹੇ ਭੇਜ ਸਕਦੇ ਹੋ। Google Messages ਤੁਹਾਡੀ ਪੂਰਵ-ਨਿਰਧਾਰਿਤ ਸੁਨੇਹਾ ਐਪ ਹੋਣੀ ਲਾਜ਼ਮੀ ਹੈ।"</string> + <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"ਸੈਟੇਲਾਈਟ ਸੁਨੇਹੇ ਦੀ ਸੁਵਿਧਾ ਸਮਰਥਿਤ ਨਹੀਂ ਹੈ"</string> + <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"ਸੈਟੇਲਾਈਟ ਸੁਨੇਹੇ ਦੀ ਸੁਵਿਧਾ ਇਸ ਡੀਵਾਈਸ \'ਤੇ ਸਮਰਥਿਤ ਨਹੀਂ ਹੈ"</string> + <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"ਸੈਟੇਲਾਈਟ ਸੁਨੇਹੇ ਦੀ ਸੁਵਿਧਾ ਦਾ ਸੈੱਟਅੱਪ ਨਹੀਂ ਕੀਤਾ ਗਿਆ ਹੈ"</string> + <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"ਪੱਕਾ ਕਰੋ ਕਿ ਤੁਸੀਂ ਇੰਟਰਨੈੱਟ ਨਾਲ ਕਨੈਕਟ ਹੋ ਅਤੇ ਦੁਬਾਰਾ ਸੈੱਟਅੱਪ ਕਰਨ ਦੀ ਕੋਸ਼ਿਸ਼ ਕਰੋ"</string> + <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_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> <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-pl/strings.xml b/core/res/res/values-pl/strings.xml index cf758feb97ae..f267877b3a18 100644 --- a/core/res/res/values-pl/strings.xml +++ b/core/res/res/values-pl/strings.xml @@ -590,7 +590,7 @@ <string name="permdesc_changeWifiMulticastState" product="tablet" msgid="191079868596433554">"Pozwala aplikacji na odbieranie pakietów wysyłanych przez sieć Wi-Fi do wszystkich urządzeń, a nie tylko do Twojego tabletu, przy użyciu adresów połączeń grupowych. Powoduje większe zapotrzebowanie na energię niż w trybie innym niż grupowy."</string> <string name="permdesc_changeWifiMulticastState" product="tv" msgid="1336952358450652595">"Pozwala aplikacji odbierać pakiety wysyłane przez sieć Wi-Fi do wszystkich urządzeń (a nie tylko do Twojego urządzenia z Androidem TV) przy użyciu adresów połączeń grupowych. Powoduje większe zapotrzebowanie na energię niż w innych trybach."</string> <string name="permdesc_changeWifiMulticastState" product="default" msgid="8296627590220222740">"Pozwala aplikacji na odbieranie pakietów wysyłanych przez sieć Wi-Fi do wszystkich urządzeń, a nie tylko do Twojego telefonu, przy użyciu adresów połączeń grupowych. Powoduje większe zapotrzebowanie na energię niż w trybie innym niż grupowy."</string> - <string name="permlab_bluetoothAdmin" msgid="6490373569441946064">"uzyskiwanie dostępu do ustawień Bluetooth"</string> + <string name="permlab_bluetoothAdmin" msgid="6490373569441946064">"uzyskiwanie dostępu do ustawień Bluetootha"</string> <string name="permdesc_bluetoothAdmin" product="tablet" msgid="5370837055438574863">"Pozwala aplikacji na konfigurowanie lokalnego tabletu z funkcją Bluetooth oraz na wykrywanie urządzeń zdalnych i parowanie z nimi."</string> <string name="permdesc_bluetoothAdmin" product="tv" msgid="1623992984547014588">"Pozwala aplikacji konfigurować Bluetootha na urządzeniu z Androidem TV oraz wykrywać urządzenia zdalne i przeprowadzać parowanie z nimi."</string> <string name="permdesc_bluetoothAdmin" product="default" msgid="7381341743021234863">"Pozwala aplikacji na konfigurowanie lokalnego telefonu z funkcją Bluetooth oraz na wykrywanie urządzeń zdalnych i parowanie z nimi."</string> @@ -614,6 +614,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Zezwól na określanie przez aplikację względnego położenia urządzeń ultraszerokopasmowych w pobliżu"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"interakcje z urządzeniami Wi-Fi w pobliżu"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Zezwala aplikacji na przesyłanie informacji o sobie, łączenie się z urządzeniami Wi‑Fi w pobliżu i określanie ich względnego położenia"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informacje o preferowanych usługach płatniczych NFC"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Pozwala aplikacji uzyskiwać informacje o preferowanych usługach płatniczych NFC, np. zarejestrowanych pomocach i miejscach docelowych tras."</string> <string name="permlab_nfc" msgid="1904455246837674977">"kontrolowanie łączności Near Field Communication"</string> @@ -2433,54 +2437,30 @@ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Włącz"</string> <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Wróć"</string> <string name="unarchival_session_app_label" msgid="6811856981546348205">"Oczekiwanie…"</string> - <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) --> - <skip /> - <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) --> - <skip /> + <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"Satelitarne połączenie alarmowe jest już dostępne"</string> + <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"Możesz wysłać wiadomość do służb ratunkowych, jeśli masz problem z połączeniem się z siecią komórkową lub Wi-Fi. Wiadomości Google muszą być domyślną aplikacją do SMS-ów."</string> + <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"Satelitarne połączenie alarmowe nie jest obsługiwane"</string> + <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"To urządzenie nie obsługuje satelitarnego połączenia alarmowego"</string> + <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"Satelitarne połączenie alarmowe nie jest skonfigurowane"</string> + <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"Upewnij się, że masz połączenie z internetem, i spróbuj ponownie"</string> + <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"Satelitarne połączenie alarmowe nie jest dostępne"</string> + <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"Satelitarne połączenie alarmowe nie jest dostępne w tym kraju lub regionie"</string> + <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"Satelitarne połączenie alarmowe nie zostało skonfigurowane"</string> + <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"Aby wysyłać wiadomości przez satelitę, ustaw Wiadomości Google jako domyślną aplikację do obsługi SMS-ów"</string> + <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"Satelitarne połączenie alarmowe nie jest dostępne"</string> + <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"Aby sprawdzić, czy satelitarne połączenie alarmowe jest dostępne w tym kraju lub regionie, włącz ustawienia lokalizacji"</string> + <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"Przesyłanie wiadomości przez satelitę jest dostępne"</string> + <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"Możesz wysyłać wiadomości przez satelitę, jeśli nie masz dostępu do sieci komórkowej lub Wi-Fi. Wiadomości Google muszą być domyślną aplikacją do SMS-ów."</string> + <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"Przesyłanie wiadomości przez satelitę nie jest obsługiwane"</string> + <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"To urządzenie nie obsługuje przesyłania wiadomości przez satelitę"</string> + <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"Nie skonfigurowano przesyłania wiadomości przez satelitę"</string> + <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"Upewnij się, że masz połączenie z internetem, i spróbuj ponownie"</string> + <string name="satellite_messaging_not_in_allowed_region_notification_title" msgid="2035303593479031655">"Przesyłanie wiadomości przez satelitę jest niedostępne"</string> + <string name="satellite_messaging_not_in_allowed_region_notification_summary" msgid="5270294879531815854">"Przesyłanie wiadomości przez satelitę nie jest dostępne w tym kraju lub regionie"</string> + <string name="satellite_messaging_unsupported_default_sms_app_notification_title" msgid="1004808759472360189">"Nie skonfigurowano przesyłania wiadomości przez satelitę"</string> + <string name="satellite_messaging_unsupported_default_sms_app_notification_summary" msgid="17084124893763593">"Aby wysyłać wiadomości przez satelitę, ustaw Wiadomości Google jako domyślną aplikację do obsługi SMS-ów"</string> + <string name="satellite_messaging_location_disabled_notification_title" msgid="7270641894250928494">"Przesyłanie wiadomości przez satelitę jest niedostępne"</string> + <string name="satellite_messaging_location_disabled_notification_summary" msgid="1450824950686221810">"Aby sprawdzić, czy przesyłanie wiadomości przez satelitę jest dostępne w tym kraju lub regionie, włącz ustawienia lokalizacji"</string> <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Skonfiguruj ponownie odblokowywanie odciskiem palca"</string> <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"Ten odcisk palca (<xliff:g id="FINGERPRINT">%s</xliff:g>) nie jest już rozpoznawany."</string> <string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"Te odciski palców (<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> i <xliff:g id="FINGERPRINT_1">%2$s</xliff:g>) nie są już rozpoznawane."</string> diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml index 71b334e89eaf..1719648d10f0 100644 --- a/core/res/res/values-pt-rBR/strings.xml +++ b/core/res/res/values-pt-rBR/strings.xml @@ -613,6 +613,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Permitir que o app determine o posicionamento relativo entre dispositivos de banda ultralarga por perto"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"interagir com dispositivos Wi-Fi por perto"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Permite que o app divulgue, faça conexão e determine a posição relativa de dispositivos Wi-Fi por perto."</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informações preferidas de serviço de pagamento por NFC"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Permite que o app acesse as informações preferidas de serviço de pagamento por NFC, como auxílios registrados ou destinos de trajetos."</string> <string name="permlab_nfc" msgid="1904455246837674977">"controlar a comunicação a curta distância"</string> @@ -1755,7 +1759,7 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Usar atalho"</string> <string name="color_inversion_feature_name" msgid="2672824491933264951">"Inversão de cores"</string> <string name="color_correction_feature_name" msgid="7975133554160979214">"Correção de cor"</string> - <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Modo uma mão"</string> + <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Modo para uma mão"</string> <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Escurecer ainda mais a tela"</string> <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Aparelhos auditivos"</string> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Teclas de volume pressionadas. Serviço <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ativado."</string> @@ -2432,54 +2436,30 @@ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Ativar"</string> <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Voltar"</string> <string name="unarchival_session_app_label" msgid="6811856981546348205">"Pendente…"</string> - <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) --> - <skip /> - <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) --> - <skip /> + <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"O SOS via satélite já está disponível"</string> + <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"Você poderá enviar uma mensagem para serviços de emergência se não houver uma rede móvel ou Wi-Fi. O Google Mensagens precisa ser seu app de mensagens padrão."</string> + <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"Incompatível com o SOS via satélite"</string> + <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"Este dispositivo não é compatível com o SOS via satélite"</string> + <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"O SOS via satélite não foi configurado"</string> + <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"Verifique sua conexão de Internet e tente configurar de novo"</string> + <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"SOS via satélite indisponível"</string> + <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"O SOS via satélite não está disponível neste país ou região"</string> + <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"SOS via satélite não configurado"</string> + <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"Para enviar mensagens via satélite, defina o Google Mensagens como seu app de mensagens padrão"</string> + <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"SOS via satélite indisponível"</string> + <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"Para verificar se o SOS via satélite está disponível neste país ou região, ative as configurações de localização"</string> + <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"Mensagem via satélite disponível"</string> + <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"É possível trocar mensagens por satélite caso não haja uma rede móvel ou Wi-Fi. O Google Mensagens precisa ser seu app de mensagens padrão."</string> + <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"Incompatível com mensagem via satélite"</string> + <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"Este dispositivo não é compatível com mensagem via satélite"</string> + <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"Mensagem via satélite não configurada"</string> + <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"Verifique sua conexão de Internet e tente configurar de novo"</string> + <string name="satellite_messaging_not_in_allowed_region_notification_title" msgid="2035303593479031655">"Mensagem via satélite indisponível"</string> + <string name="satellite_messaging_not_in_allowed_region_notification_summary" msgid="5270294879531815854">"A mensagem via satélite não está disponível neste país ou região"</string> + <string name="satellite_messaging_unsupported_default_sms_app_notification_title" msgid="1004808759472360189">"Mensagem via satélite não configurada"</string> + <string name="satellite_messaging_unsupported_default_sms_app_notification_summary" msgid="17084124893763593">"Para enviar mensagens via satélite, defina o Google Mensagens como seu app de mensagens padrão"</string> + <string name="satellite_messaging_location_disabled_notification_title" msgid="7270641894250928494">"Mensagem via satélite indisponível"</string> + <string name="satellite_messaging_location_disabled_notification_summary" msgid="1450824950686221810">"Para verificar se a mensagem via satélite está disponível neste país ou região, ative as configurações de localização"</string> <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Configurar o Desbloqueio por impressão digital de novo"</string> <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"A impressão digital <xliff:g id="FINGERPRINT">%s</xliff:g> não é mais reconhecida."</string> <string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"As impressões digitais <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> e <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> não são mais reconhecidas."</string> diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml index 4d5ff474c84f..b8fc6a96f783 100644 --- a/core/res/res/values-pt-rPT/strings.xml +++ b/core/res/res/values-pt-rPT/strings.xml @@ -613,6 +613,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Permita que a app determine a posição relativa entre os dispositivos de banda ultralarga próximos"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"interagir com dispositivos Wi‑Fi próximos"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Permite que a app anuncie, estabeleça ligação e determine a posição relativa de dispositivos Wi‑Fi próximos"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informações de serviços de pagamento com NFC preferenciais"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Permite que a app obtenha informações de serviços de pagamento com NFC preferenciais, como apoios registados e destino da rota."</string> <string name="permlab_nfc" msgid="1904455246837674977">"controlo Near Field Communication"</string> @@ -2432,54 +2436,30 @@ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Ativar"</string> <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Retroceder"</string> <string name="unarchival_session_app_label" msgid="6811856981546348205">"Pendente…"</string> - <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) --> - <skip /> - <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) --> - <skip /> + <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"O Satélite SOS já está disponível"</string> + <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"Pode enviar mensagens aos serviços de emergência se não tiver uma rede móvel nem Wi-Fi. A app Mensagens Google tem de ser a sua app de mensagens predefinida."</string> + <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"O Satélite SOS não é compatível"</string> + <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"O Satélite SOS não é compatível com este dispositivo"</string> + <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"O Satélite SOS não está configurado"</string> + <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"Certifique-se de que tem ligação à Internet e tente configurar novamente"</string> + <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"O Satélite SOS não está disponível"</string> + <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"O Satélite SOS não está disponível neste país ou região"</string> + <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"Satélite SOS não configurado"</string> + <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"Para enviar mensagens por satélite, defina a app Mensagens Google como a sua app de mensagens predefinida"</string> + <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"O Satélite SOS não está disponível"</string> + <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"Para verificar se o Satélite SOS está disponível neste país ou região, ative as definições de localização"</string> + <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"Mensagens por satélite disponíveis"</string> + <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"Pode enviar mensagens por satélite se não tiver uma rede móvel nem Wi-Fi. A app Mensagens Google tem de ser a sua app de mensagens predefinida."</string> + <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"As mensagens por satélite não são compatíveis"</string> + <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"As mensagens por satélite não são compatíveis com este dispositivo"</string> + <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"Mensagens por satélite não configuradas"</string> + <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"Certifique-se de que tem ligação à Internet e tente configurar novamente"</string> + <string name="satellite_messaging_not_in_allowed_region_notification_title" msgid="2035303593479031655">"Mensagens por satélite não disponíveis"</string> + <string name="satellite_messaging_not_in_allowed_region_notification_summary" msgid="5270294879531815854">"As mensagens por satélite não estão disponíveis neste país ou região"</string> + <string name="satellite_messaging_unsupported_default_sms_app_notification_title" msgid="1004808759472360189">"Mensagens por satélite não configuradas"</string> + <string name="satellite_messaging_unsupported_default_sms_app_notification_summary" msgid="17084124893763593">"Para enviar mensagens por satélite, defina a app Mensagens Google como a sua app de mensagens predefinida"</string> + <string name="satellite_messaging_location_disabled_notification_title" msgid="7270641894250928494">"Mensagens por satélite não disponíveis"</string> + <string name="satellite_messaging_location_disabled_notification_summary" msgid="1450824950686221810">"Para verificar se as mensagens por satélite estão disponíveis neste país ou região, ative as definições de localização"</string> <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Configure o Desbloqueio por impressão digital novamente"</string> <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"Já não é possível reconhecer <xliff:g id="FINGERPRINT">%s</xliff:g>."</string> <string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"Já não é possível reconhecer <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> e <xliff:g id="FINGERPRINT_1">%2$s</xliff:g>."</string> diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml index 71b334e89eaf..1719648d10f0 100644 --- a/core/res/res/values-pt/strings.xml +++ b/core/res/res/values-pt/strings.xml @@ -613,6 +613,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Permitir que o app determine o posicionamento relativo entre dispositivos de banda ultralarga por perto"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"interagir com dispositivos Wi-Fi por perto"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Permite que o app divulgue, faça conexão e determine a posição relativa de dispositivos Wi-Fi por perto."</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informações preferidas de serviço de pagamento por NFC"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Permite que o app acesse as informações preferidas de serviço de pagamento por NFC, como auxílios registrados ou destinos de trajetos."</string> <string name="permlab_nfc" msgid="1904455246837674977">"controlar a comunicação a curta distância"</string> @@ -1755,7 +1759,7 @@ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Usar atalho"</string> <string name="color_inversion_feature_name" msgid="2672824491933264951">"Inversão de cores"</string> <string name="color_correction_feature_name" msgid="7975133554160979214">"Correção de cor"</string> - <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Modo uma mão"</string> + <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Modo para uma mão"</string> <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Escurecer ainda mais a tela"</string> <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Aparelhos auditivos"</string> <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Teclas de volume pressionadas. Serviço <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ativado."</string> @@ -2432,54 +2436,30 @@ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Ativar"</string> <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Voltar"</string> <string name="unarchival_session_app_label" msgid="6811856981546348205">"Pendente…"</string> - <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) --> - <skip /> - <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) --> - <skip /> + <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"O SOS via satélite já está disponível"</string> + <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"Você poderá enviar uma mensagem para serviços de emergência se não houver uma rede móvel ou Wi-Fi. O Google Mensagens precisa ser seu app de mensagens padrão."</string> + <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"Incompatível com o SOS via satélite"</string> + <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"Este dispositivo não é compatível com o SOS via satélite"</string> + <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"O SOS via satélite não foi configurado"</string> + <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"Verifique sua conexão de Internet e tente configurar de novo"</string> + <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"SOS via satélite indisponível"</string> + <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"O SOS via satélite não está disponível neste país ou região"</string> + <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"SOS via satélite não configurado"</string> + <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"Para enviar mensagens via satélite, defina o Google Mensagens como seu app de mensagens padrão"</string> + <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"SOS via satélite indisponível"</string> + <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"Para verificar se o SOS via satélite está disponível neste país ou região, ative as configurações de localização"</string> + <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"Mensagem via satélite disponível"</string> + <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"É possível trocar mensagens por satélite caso não haja uma rede móvel ou Wi-Fi. O Google Mensagens precisa ser seu app de mensagens padrão."</string> + <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"Incompatível com mensagem via satélite"</string> + <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"Este dispositivo não é compatível com mensagem via satélite"</string> + <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"Mensagem via satélite não configurada"</string> + <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"Verifique sua conexão de Internet e tente configurar de novo"</string> + <string name="satellite_messaging_not_in_allowed_region_notification_title" msgid="2035303593479031655">"Mensagem via satélite indisponível"</string> + <string name="satellite_messaging_not_in_allowed_region_notification_summary" msgid="5270294879531815854">"A mensagem via satélite não está disponível neste país ou região"</string> + <string name="satellite_messaging_unsupported_default_sms_app_notification_title" msgid="1004808759472360189">"Mensagem via satélite não configurada"</string> + <string name="satellite_messaging_unsupported_default_sms_app_notification_summary" msgid="17084124893763593">"Para enviar mensagens via satélite, defina o Google Mensagens como seu app de mensagens padrão"</string> + <string name="satellite_messaging_location_disabled_notification_title" msgid="7270641894250928494">"Mensagem via satélite indisponível"</string> + <string name="satellite_messaging_location_disabled_notification_summary" msgid="1450824950686221810">"Para verificar se a mensagem via satélite está disponível neste país ou região, ative as configurações de localização"</string> <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Configurar o Desbloqueio por impressão digital de novo"</string> <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"A impressão digital <xliff:g id="FINGERPRINT">%s</xliff:g> não é mais reconhecida."</string> <string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"As impressões digitais <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> e <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> não são mais reconhecidas."</string> diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml index 6e43d317e0f0..7843bbe1e9f1 100644 --- a/core/res/res/values-ro/strings.xml +++ b/core/res/res/values-ro/strings.xml @@ -613,6 +613,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Permite-i aplicației să stabilească poziția relativă dintre dispozitivele Ultra-Wideband din apropiere"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"să interacționeze cu dispozitive Wi‑Fi din apropiere"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Permite aplicației să se conecteze la dispozitive Wi-Fi din apropiere, să transmită anunțuri și să stabilească poziția relativă a acestora"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informații despre serviciul de plăți NFC preferat"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Permite aplicației să obțină informații despre serviciul de plăți NFC preferat, de exemplu, identificatorii de aplicație înregistrați și destinația traseului."</string> <string name="permlab_nfc" msgid="1904455246837674977">"controlare schimb de date prin Near Field Communication"</string> @@ -2432,54 +2436,30 @@ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Activează"</string> <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Înapoi"</string> <string name="unarchival_session_app_label" msgid="6811856981546348205">"În așteptare..."</string> - <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) --> - <skip /> - <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) --> - <skip /> + <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"Funcția SOS prin satelit este acum disponibilă"</string> + <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"Poți să trimiți mesaje serviciilor de urgență dacă nu este disponibilă o rețea mobilă sau Wi-Fi. Mesaje Google trebuie să fie aplicația ta pentru mesaje prestabilită."</string> + <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"Funcția SOS prin satelit nu este acceptată"</string> + <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"Funcția SOS prin satelit nu este acceptată pe acest dispozitiv"</string> + <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"Funcția SOS prin satelit nu este configurată"</string> + <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"Asigură-te că te-ai conectat la internet și încearcă din nou configurarea"</string> + <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"Funcția SOS prin satelit nu este disponibilă"</string> + <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"Funcția SOS prin satelit nu este disponibilă în această țară sau regiune"</string> + <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"Funcția SOS prin satelit nu este configurată"</string> + <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"Pentru a trimite mesaje prin satelit, setează Mesaje Google ca aplicație pentru mesaje prestabilită"</string> + <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"Funcția SOS prin satelit nu este disponibilă"</string> + <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"Pentru a verifica dacă funcția SOS prin satelit este disponibilă în această țară sau regiune, activează setările privind locația"</string> + <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"Mesajele prin satelit sunt disponibile"</string> + <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"Dacă nu este disponibilă o rețea mobilă sau Wi-Fi, poți să trimiți mesaje prin satelit. Mesaje Google trebuie să fie aplicația ta pentru mesaje prestabilită."</string> + <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"Mesajele prin satelit nu sunt acceptate"</string> + <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"Mesajele prin satelit nu sunt acceptate pe acest dispozitiv"</string> + <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"Mesajele prin satelit nu sunt configurate"</string> + <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"Asigură-te că te-ai conectat la internet și încearcă din nou configurarea"</string> + <string name="satellite_messaging_not_in_allowed_region_notification_title" msgid="2035303593479031655">"Mesajele prin satelit nu sunt disponibile"</string> + <string name="satellite_messaging_not_in_allowed_region_notification_summary" msgid="5270294879531815854">"Mesajele prin satelit nu sunt disponibile în această țară sau regiune"</string> + <string name="satellite_messaging_unsupported_default_sms_app_notification_title" msgid="1004808759472360189">"Mesajele prin satelit nu sunt configurate"</string> + <string name="satellite_messaging_unsupported_default_sms_app_notification_summary" msgid="17084124893763593">"Pentru a trimite mesaje prin satelit, setează Mesaje Google ca aplicație pentru mesaje prestabilită"</string> + <string name="satellite_messaging_location_disabled_notification_title" msgid="7270641894250928494">"Mesajele prin satelit nu sunt disponibile"</string> + <string name="satellite_messaging_location_disabled_notification_summary" msgid="1450824950686221810">"Pentru a verifica dacă mesajele prin satelit sunt disponibile în această țară sau regiune, activează setările privind locația"</string> <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Configurează din nou Deblocarea cu amprenta"</string> <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> nu mai poate fi recunoscută."</string> <string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> și <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> nu mai pot fi recunoscute."</string> diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml index b23a44bcc9fc..8caa8b60c120 100644 --- a/core/res/res/values-ru/strings.xml +++ b/core/res/res/values-ru/strings.xml @@ -614,6 +614,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Приложение сможет определять относительное позиционирование устройств с технологией сверхширокополосной связи поблизости"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"Взаимодействие с устройствами Wi‑Fi поблизости"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Приложение сможет передавать данные на устройства Wi‑Fi рядом, подключаться к ним и определять их примерное местоположение."</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Сведения о предпочтительном платежном сервисе NFC"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Приложение сможет получать сведения о предпочтительном платежном сервисе NFC (например, зарегистрированные идентификаторы AID и конечный пункт маршрута)."</string> <string name="permlab_nfc" msgid="1904455246837674977">"Управление NFC-модулем"</string> @@ -2433,54 +2437,30 @@ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Включить"</string> <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Назад"</string> <string name="unarchival_session_app_label" msgid="6811856981546348205">"Обработка…"</string> - <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) --> - <skip /> - <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) --> - <skip /> + <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"Доступен спутниковый SOS"</string> + <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"Вы можете отправлять сообщения экстренным службам, даже когда подключение по мобильной сети или Wi-Fi недоступно. Google Сообщения должны быть выбраны в качестве мессенджера по умолчанию."</string> + <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"Спутниковый SOS не поддерживается"</string> + <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"Функция недоступна на этом устройстве."</string> + <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"Спутниковый SOS не настроен"</string> + <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"Проверьте подключение к интернету и повторите попытку."</string> + <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"Спутниковый SOS недоступен"</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">"Спутниковый SOS не настроен"</string> + <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"Чтобы использовать эту функцию, необходимо выбрать Google Сообщения в качестве мессенджера по умолчанию."</string> + <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"Спутниковый SOS недоступен"</string> + <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"Чтобы узнать, можно ли использовать спутниковый SOS в этой стране или регионе, включите настройки геолокации."</string> + <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"Доступен спутниковый обмен сообщениями"</string> + <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"Вы можете обмениваться сообщениями по спутниковой связи, даже когда подключение к мобильной сети или Wi-Fi недоступно. Google Сообщения должны быть выбраны в качестве мессенджера по умолчанию."</string> + <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"Спутниковый обмен сообщениями не поддерживается"</string> + <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"Функция недоступна на этом устройстве."</string> + <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"Спутниковый обмен сообщениями не настроен"</string> + <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"Проверьте подключение к интернету и повторите попытку."</string> + <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 Сообщения в качестве мессенджера по умолчанию."</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> <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-si/strings.xml b/core/res/res/values-si/strings.xml index db82c6c98fa4..f150cd189f45 100644 --- a/core/res/res/values-si/strings.xml +++ b/core/res/res/values-si/strings.xml @@ -612,6 +612,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"අවට ඇති අල්ට්රා-වයිඩ්බෑන්ඩ් උපාංග අතර සාපේක්ෂ පිහිටීම නිර්ණය කිරීමට යෙදුමට ඉඩ දීම"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"අවට Wi‑Fi උපාංග සමග අන්තර්ක්රියා කරන්න"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"වෙළඳ දැන්වීම් පළ කිරීමට, සම්බන්ධ වීමට සහ අවට ඇති Wi-Fi උපාංගවල සාපේක්ෂ පිහිටීම නිර්ණය කිරීමට යෙදුමට ඉඩ දෙයි"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"කැමති NFC ගෙවීම් සේවා තොරතුරු"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"ලියාපදිංචි කළ ආධාර සහ ගමන් මාර්ග ගමනාන්ත වැනි කැමති nfc ගෙවීම් සේවා තොරතුරු ලබා ගැනීමට යෙදුමට ඉඩ දෙයි."</string> <string name="permlab_nfc" msgid="1904455246837674977">"ආසන්න ක්ෂේත්ර සන්නිවේදනය පාලනය කරන්න"</string> diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml index cbabfb6e1076..e17e25f4734d 100644 --- a/core/res/res/values-sk/strings.xml +++ b/core/res/res/values-sk/strings.xml @@ -614,6 +614,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Povoľte aplikácii určovať relatívnu polohu medzi zariadeniami s ultraširokopásmovým pripojením v okolí"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"interakcia so zariadeniami Wi-Fi v okolí"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Umožňuje aplikácii oznamovať a rozpoznávať relatívnu polohu zariadení Wi‑Fi v okolí a pripájať sa k nim"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Preferované informácie platenej služby NFC"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Umožňuje aplikácii získavať preferované informácie platenej služby NFC, napríklad o registrovanej pomoci a trasách k cieľu."</string> <string name="permlab_nfc" msgid="1904455246837674977">"ovládať technológiu NFC"</string> diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml index d7178c802ec6..da9f1c189610 100644 --- a/core/res/res/values-sl/strings.xml +++ b/core/res/res/values-sl/strings.xml @@ -614,6 +614,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Aplikaciji dovoli, da določi relativno oddaljenost med napravami UWB v bližini."</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"komunikacija z napravami Wi‑Fi v bližini"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Aplikaciji dovoljuje objavljanje in določanje relativnega položaja naprav Wi‑Fi v bližini ter povezovanje z njimi."</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Podatki o prednostni storitvi za plačevanje prek povezave NFC"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Aplikaciji omogoča pridobivanje podatkov o prednostni storitvi za plačevanje prek povezave NFC, kot so registrirani pripomočki in cilj preusmeritve."</string> <string name="permlab_nfc" msgid="1904455246837674977">"nadzor nad komunikacijo s tehnologijo bližnjega polja"</string> @@ -2433,54 +2437,30 @@ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Vklopi"</string> <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Nazaj"</string> <string name="unarchival_session_app_label" msgid="6811856981546348205">"V teku …"</string> - <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) --> - <skip /> - <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) --> - <skip /> + <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"SOS prek satelita je zdaj na voljo"</string> + <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"Reševalnim službam lahko pošljete sporočilo, če ni povezave z mobilnim omrežjem ali omrežjem Wi-Fi. Google Sporočila morajo biti privzeta aplikacija za sporočanje."</string> + <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"SOS prek satelita ni podprt"</string> + <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"SOS prek satelita ni podprt v tej napravi"</string> + <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"SOS prek satelita ni nastavljen"</string> + <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"Zagotovite, da imate vzpostavljeno internetno povezavo, in znova poskusite nastaviti"</string> + <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"SOS prek satelita ni na voljo"</string> + <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"SOS prek satelita ni na voljo v tej državi ali regiji"</string> + <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"SOS prek satelita ni nastavljen"</string> + <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"Če želite pošiljati satelitska sporočila, nastavite Google Sporočila kot privzeto aplikacijo za sporočanje"</string> + <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"SOS prek satelita ni na voljo"</string> + <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"Če želite preveriti, ali je SOS prek satelita na voljo v tej državi ali regiji, vklopite nastavitve lokacije"</string> + <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"Satelitska sporočila so na voljo"</string> + <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"Satelitska sporočila lahko pošljete, če ni povezave z mobilnim omrežjem ali omrežjem Wi-Fi. Google Sporočila morajo biti privzeta aplikacija za sporočanje."</string> + <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"Satelitska sporočila niso podprta"</string> + <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"Satelitska sporočila niso podprta v tej napravi"</string> + <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"Satelitska sporočila niso nastavljena"</string> + <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"Zagotovite, da imate vzpostavljeno internetno povezavo, in znova poskusite nastaviti"</string> + <string name="satellite_messaging_not_in_allowed_region_notification_title" msgid="2035303593479031655">"Satelitska sporočila niso na voljo"</string> + <string name="satellite_messaging_not_in_allowed_region_notification_summary" msgid="5270294879531815854">"Satelitska sporočila niso na voljo v tej državi ali regiji"</string> + <string name="satellite_messaging_unsupported_default_sms_app_notification_title" msgid="1004808759472360189">"Satelitska sporočila niso nastavljena"</string> + <string name="satellite_messaging_unsupported_default_sms_app_notification_summary" msgid="17084124893763593">"Če želite pošiljati satelitska sporočila, nastavite Google Sporočila kot privzeto aplikacijo za sporočanje"</string> + <string name="satellite_messaging_location_disabled_notification_title" msgid="7270641894250928494">"Satelitska sporočila niso na voljo"</string> + <string name="satellite_messaging_location_disabled_notification_summary" msgid="1450824950686221810">"Če želite preveriti, ali so satelitska sporočila na voljo v tej državi ali regiji, vklopite nastavitve lokacije"</string> <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Vnovična nastavitev odklepanja s prstnim odtisom"</string> <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"Odtisa »<xliff:g id="FINGERPRINT">%s</xliff:g>« ni več mogoče prepoznati."</string> <string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"Odtisov »<xliff:g id="FINGERPRINT_0">%1$s</xliff:g>« in »<xliff:g id="FINGERPRINT_1">%2$s</xliff:g>« ni več mogoče prepoznati."</string> diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml index 42356bf8cc07..1b8d0a0095f3 100644 --- a/core/res/res/values-sq/strings.xml +++ b/core/res/res/values-sq/strings.xml @@ -612,6 +612,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Lejo që aplikacioni të përcaktojë pozicionin e përafërt mes pajisjeve në afërsi me brezin ultra të gjerë"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"të ndërveprojë me pajisjet Wi-Fi në afërsi"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Lejon që aplikacioni të reklamojë, të lidhet dhe të përcaktojë pozicionin përkatës të pajisjeve Wi-Fi në afërsi"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informacionet për shërbimin e preferuar të pagesës me NFC"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Lejon aplikacionin të marrë informacione për shërbimin e preferuar të pagesës me NFC si p.sh. ndihmat e regjistruara dhe destinacionin e itinerarit."</string> <string name="permlab_nfc" msgid="1904455246837674977">"kontrollo \"Komunikimin e fushës në afërsi\" NFC"</string> @@ -1663,7 +1667,7 @@ <string name="default_audio_route_name_headphones" msgid="6954070994792640762">"Kufjet"</string> <string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string> <string name="default_audio_route_category_name" msgid="5241740395748134483">"Sistemi"</string> - <string name="bluetooth_a2dp_audio_route_name" msgid="4214648773120426288">"Audioja e \"bluetooth-it\""</string> + <string name="bluetooth_a2dp_audio_route_name" msgid="4214648773120426288">"Audioja e Bluetooth-it"</string> <string name="wireless_display_route_description" msgid="8297563323032966831">"Ekran wireless"</string> <string name="media_route_button_content_description" msgid="2299223698196869956">"Transmeto"</string> <string name="media_route_chooser_title" msgid="6646594924991269208">"Lidhu me pajisjen"</string> diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml index 10fa5f831fb4..bdf557173391 100644 --- a/core/res/res/values-sr/strings.xml +++ b/core/res/res/values-sr/strings.xml @@ -613,6 +613,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Дозвољава апликацији да одређује релативну раздаљину између уређаја ултра-широког појаса у близини"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"интеракција са WiFi уређајима у близини"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Дозвољава апликацији да се оглашава, повезује и утврђује релативну позицију WiFi уређаја у близини"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Информације о жељеној NFC услузи за плаћање"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Дозвољава апликацији да преузима информације о жељеној NFC услузи за плаћање, попут регистрованих идентификатора апликација и одредишта преусмеравања."</string> <string name="permlab_nfc" msgid="1904455246837674977">"контрола комуникације у ужем пољу (Near Field Communication)"</string> @@ -2432,54 +2436,30 @@ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Укључи"</string> <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Назад"</string> <string name="unarchival_session_app_label" msgid="6811856981546348205">"На чекању..."</string> - <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) --> - <skip /> - <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) --> - <skip /> + <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"Хитна помоћ преко сателита је сада доступна"</string> + <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"Можете да шаљете поруке хитним службама ако немате приступ мобилној ни WiFi мрежи. Google Messages мора да буде подразумевана апликација за размену порука."</string> + <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"Хитна помоћ преко сателита није подржана"</string> + <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"Хитна помоћ преко сателита није подржанa на овом уређају"</string> + <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"Хитна помоћ преко сателита није подешена"</string> + <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"Проверите да ли сте повезани на интернет и пробајте поново да подесите"</string> + <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_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> + <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"Можете да шаљете поруке преко сателита ако немате приступ мобилној ни WiFi мрежи. Google Messages мора да буде подразумевана апликација за размену порука."</string> + <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"Размена порука преко сателита није подржана"</string> + <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"Размена порука преко сателита није подржана на овом уређају"</string> + <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"Размена порука преко сателита није подешена"</string> + <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"Проверите да ли сте повезани на интернет и пробајте поново да подесите"</string> + <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_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> <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-sv/strings.xml b/core/res/res/values-sv/strings.xml index 857b060452ec..8bef5ca8a842 100644 --- a/core/res/res/values-sv/strings.xml +++ b/core/res/res/values-sv/strings.xml @@ -612,6 +612,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Tillåt att appen fastställer den relativa positionen mellan Ultra Wideband-enheter i närheten"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"interagera med wifi-enheter i närheten"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Tillåter appen att sända ut till, ansluta till och fastställa relativ position för wifi-enheter i närheten"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Information kopplad till standardtjänsten för NFC-betalning"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Tillåter att appen hämtar information kopplad till standardtjänsten för NFC-betalning, till exempel registrerade hjälpmedel och ruttdestinationer."</string> <string name="permlab_nfc" msgid="1904455246837674977">"kontrollera närfältskommunikationen"</string> @@ -2431,54 +2435,30 @@ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Aktivera"</string> <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Tillbaka"</string> <string name="unarchival_session_app_label" msgid="6811856981546348205">"Väntar …"</string> - <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) --> - <skip /> - <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) --> - <skip /> + <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"SOS-larm via satellit är nu tillgängligt"</string> + <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"Du kan skicka meddelanden till räddningstjänsten om det inte finns någon mobil- eller wifi-anslutning. Google Messages måste vara din standardapp för meddelanden."</string> + <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"SOS-larm via satellit stöds inte"</string> + <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"SOS-larm via satellit stöds inte på den här enheten"</string> + <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"SOS-larm via satellit har inte konfigurerats"</string> + <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"Kontrollera att enheten är ansluten till internet och försök igen"</string> + <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"SOS-larm via satellit är inte tillgängligt"</string> + <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"SOS-larm via satellit är inte tillgängligt i det här landet eller den här regionen"</string> + <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"SOS-larm via satellit har inte konfigurerats"</string> + <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"Ställ in Google Messages som standardapp för meddelanden om du vill skicka meddelanden via satellit"</string> + <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"SOS-larm via satellit är inte tillgängligt"</string> + <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"Aktivera platsinställningar för att kontrollera om SOS-larm via satellit är tillgängligt i det här landet eller den här regionen"</string> + <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"Satellitmeddelanden är tillgängliga"</string> + <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"Du kan skicka meddelanden via satellit om det inte finns någon mobil- eller wifi-anslutning. Google Messages måste vara din standardapp för meddelanden."</string> + <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"Satellitmeddelanden stöds inte"</string> + <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"Satellitmeddelanden stöds inte på den här enheten"</string> + <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"Satellitmeddelanden har inte ställts in"</string> + <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"Kontrollera att enheten är ansluten till internet och försök igen"</string> + <string name="satellite_messaging_not_in_allowed_region_notification_title" msgid="2035303593479031655">"Satellitmeddelanden är inte tillgängliga"</string> + <string name="satellite_messaging_not_in_allowed_region_notification_summary" msgid="5270294879531815854">"Satellitmeddelanden är inte tillgängliga i det här landet eller den här regionen"</string> + <string name="satellite_messaging_unsupported_default_sms_app_notification_title" msgid="1004808759472360189">"Satellitmeddelanden har inte ställts in"</string> + <string name="satellite_messaging_unsupported_default_sms_app_notification_summary" msgid="17084124893763593">"Ställ in Google Messages som standardapp för meddelanden om du vill skicka meddelanden via satellit"</string> + <string name="satellite_messaging_location_disabled_notification_title" msgid="7270641894250928494">"Satellitmeddelanden är inte tillgängliga"</string> + <string name="satellite_messaging_location_disabled_notification_summary" msgid="1450824950686221810">"Aktivera platsinställningar för att kontrollera om satellitmeddelanden är tillgängliga i det här landet eller den här regionen"</string> <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Konfigurera fingeravtryckslås igen"</string> <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"Det går inte längre att känna igen <xliff:g id="FINGERPRINT">%s</xliff:g>."</string> <string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"Det går inte längre att känna igen <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> och <xliff:g id="FINGERPRINT_1">%2$s</xliff:g>."</string> diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml index d46fa084258b..010415eaf313 100644 --- a/core/res/res/values-sw/strings.xml +++ b/core/res/res/values-sw/strings.xml @@ -612,6 +612,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Ruhusu programu ibainishe nafasi kati ya vifaa vyenye Bendi Pana Zaidi vilivyo karibu"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"tumia vifaa vya Wi‑Fi vilivyo karibu"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Huruhusu programu kutangaza, kuunganisha na kubaini mahali palipokadiriwa vilipo vifaa vya Wi-Fi vilivyo karibu"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Maelezo ya Huduma Inayopendelewa ya Malipo ya NFC"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Huruhusu programu kupata maelezo ya huduma inayopendelewa ya malipo ya nfc kama vile huduma zilizosajiliwa na njia."</string> <string name="permlab_nfc" msgid="1904455246837674977">"kudhibiti Mawasiliano ya Vifaa Vilivyokaribu (NFC)"</string> @@ -2405,7 +2409,7 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Muundo wa kibodi umewekwa kuwa <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g>… Gusa ili ubadilishe."</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Mipangilio ya kibodi halisi imewekwa"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Gusa ili uangalie kibodi"</string> - <string name="profile_label_private" msgid="6463418670715290696">"Faragha"</string> + <string name="profile_label_private" msgid="6463418670715290696">"Sehemu ya Faragha"</string> <string name="profile_label_clone" msgid="769106052210954285">"Nakala"</string> <string name="profile_label_work" msgid="3495359133038584618">"Kazini"</string> <string name="profile_label_work_2" msgid="4691533661598632135">"Wa 2 wa Kazini"</string> diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml index 32fefacb1115..1915f2982611 100644 --- a/core/res/res/values-ta/strings.xml +++ b/core/res/res/values-ta/strings.xml @@ -612,6 +612,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"அருகிலுள்ள அல்ட்ரா-வைடுபேண்ட் சாதனங்களுக்கிடையிலான தூரத்தைத் தீர்மானிக்க ஆப்ஸை அனுமதிக்கும்"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"அருகிலுள்ள வைஃபை சாதனங்களுடன் தொடர்பு கொள்ளுதல்"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"அருகிலுள்ள வைஃபை சாதனங்களைத் தெரியப்படுத்தவும் இணைக்கவும் இருப்பிடத்தைத் தீர்மானிக்கவும் இது ஆப்ஸை அனுமதிக்கும்"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"விருப்பமான NFC பேமெண்ட் சேவை தொடர்பான தகவல்கள்"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"பதிவுசெய்யப்பட்ட கருவிகள், சேருமிடத்திற்கான வழி போன்ற விருப்பமான NFC பேமெண்ட் சேவை தொடர்பான தகவல்களைப் பெற ஆப்ஸை அனுமதிக்கிறது."</string> <string name="permlab_nfc" msgid="1904455246837674977">"குறுகிய இடைவெளி தகவல்பரிமாற்றத்தைக் கட்டுப்படுத்துதல்"</string> diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml index 8500e9dcd0d7..6ac81642d41f 100644 --- a/core/res/res/values-te/strings.xml +++ b/core/res/res/values-te/strings.xml @@ -612,6 +612,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"సమీపంలోని అల్ట్రా-వైడ్బ్యాండ్ పరికరాల మధ్య సాపేక్ష స్థానాన్ని నిర్ణయించడానికి యాప్ను అనుమతించండి"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"సమీపంలోని Wi-Fi పరికరాలతో ఇంటరాక్ట్ చేస్తుంది"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"అడ్వర్టయిజ్, కనెక్ట్ చేయడానికి, సమీపంలోని Wi-Fi పరికరాల సంబంధిత పొజిషన్ను నిర్ణయించడానికి యాప్ను అనుమతిస్తుంది"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"ప్రాధాన్యత ఇవ్వబడిన NFC చెల్లింపు సేవల సమాచారం"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"ప్రాధాన్యత ఇవ్వబడిన NFC చెల్లింపు సేవల సమాచారాన్ని, అంటే రిజిస్టర్ చేయబడిన సహాయక సాధనాలు, మార్గం, గమ్యస్థానం వంటి వాటిని పొందేందుకు యాప్ను అనుమతిస్తుంది."</string> <string name="permlab_nfc" msgid="1904455246837674977">"సమీప క్షేత్ర కమ్యూనికేషన్ను నియంత్రించడం"</string> diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml index a1c7b4f59608..763eb6266198 100644 --- a/core/res/res/values-th/strings.xml +++ b/core/res/res/values-th/strings.xml @@ -612,6 +612,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"อนุญาตให้แอประบุตำแหน่งสัมพันธ์ระหว่างอุปกรณ์ที่ใช้แถบความถี่กว้างยิ่งยวดซึ่งอยู่ใกล้เคียง"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"โต้ตอบกับอุปกรณ์ Wi-Fi ที่อยู่ใกล้เคียง"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"อนุญาตให้แอปแสดงข้อมูล เชื่อมต่อ และระบุตำแหน่งซึ่งสัมพันธ์กับอุปกรณ์ Wi-Fi ที่อยู่ใกล้เคียง"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"ข้อมูลบริการชำระเงิน NFC ที่ต้องการ"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"อนุญาตให้แอปรับข้อมูลบริการชำระเงิน NFC ที่ต้องการ เช่น รหัสแอป (AID) ที่ลงทะเบียนและปลายทางของเส้นทาง"</string> <string name="permlab_nfc" msgid="1904455246837674977">"ควบคุม Near Field Communication"</string> @@ -2431,54 +2435,30 @@ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"เปิด"</string> <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"ย้อนกลับ"</string> <string name="unarchival_session_app_label" msgid="6811856981546348205">"รอดำเนินการ..."</string> - <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) --> - <skip /> - <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) --> - <skip /> + <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"SOS ดาวเทียมพร้อมใช้งานแล้ว"</string> + <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"คุณส่งข้อความหาบริการช่วยเหลือฉุกเฉินได้ในกรณีที่ไม่มีเครือข่ายมือถือหรือ Wi-Fi โดยที่แอปรับส่งข้อความเริ่มต้นของคุณจะต้องเป็น Google Messages"</string> + <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"ไม่รองรับ SOS ดาวเทียม"</string> + <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"อุปกรณ์นี้ไม่รองรับ SOS ดาวเทียม"</string> + <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"ไม่ได้ตั้งค่า SOS ดาวเทียม"</string> + <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"ตรวจสอบว่าคุณได้เชื่อมต่ออินเทอร์เน็ตแล้ว และลองตั้งค่าอีกครั้ง"</string> + <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"SOS ดาวเทียมไม่พร้อมใช้งาน"</string> + <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"SOS ดาวเทียมไม่พร้อมใช้งานในประเทศหรือภูมิภาคนี้"</string> + <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"ไม่ได้ตั้งค่า SOS ดาวเทียม"</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">"SOS ดาวเทียมไม่พร้อมใช้งาน"</string> + <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"หากต้องการตรวจสอบว่า 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> + <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"อุปกรณ์นี้ไม่รองรับการรับส่งข้อความผ่านดาวเทียม"</string> + <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"ไม่ได้ตั้งค่าการรับส่งข้อความผ่านดาวเทียม"</string> + <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"ตรวจสอบว่าคุณได้เชื่อมต่ออินเทอร์เน็ตแล้ว และลองตั้งค่าอีกครั้ง"</string> + <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_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> <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-tl/strings.xml b/core/res/res/values-tl/strings.xml index f15d952db15a..b44faae4e84f 100644 --- a/core/res/res/values-tl/strings.xml +++ b/core/res/res/values-tl/strings.xml @@ -612,6 +612,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Payagan ang app na tukuyin ang relatibong posisyon sa pagitan ng mga kalapit na Ultra-Wideband device"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"makipag-ugnayan sa mga kalapit na Wi‑Fi device"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Nagbibigay-daan sa app na i-advertise ang, kumonekta sa, at tukuyin ang nauugnay na posisyon ng mga kalapit na Wi‑Fi device"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Impormasyon sa Gustong NFC na Serbisyo sa Pagbabayad"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Pinapayagan ang app na makakuha ng impormasyon sa gustong nfc na serbisyo sa pagbabayad tulad ng mga nakarehistrong application ID at destinasyon ng ruta."</string> <string name="permlab_nfc" msgid="1904455246837674977">"kontrolin ang Near Field Communication"</string> @@ -2431,54 +2435,30 @@ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"I-on"</string> <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Bumalik"</string> <string name="unarchival_session_app_label" msgid="6811856981546348205">"Nakabinbin..."</string> - <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) --> - <skip /> - <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) --> - <skip /> + <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"Available na ang SOS gamit ang Satellite"</string> + <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"Puwede kang magpadala ng mensahe sa mga serbisyong pang-emergency kung walang mobile o Wi-Fi network. Dapat Google Messages ang default mong app sa pagmemensahe."</string> + <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"Hindi sinusuportahan ang SOS gamit ang satellite"</string> + <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"Hindi sinusuportahan ang SOS gamit ang satellite sa device na ito"</string> + <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"Hindi naka-set up ang SOS gamit ang satellite"</string> + <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"Tiyaking nakakonekta ka sa internet at subukang mag-set up ulit"</string> + <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"Hindi available ang SOS gamit ang satellite"</string> + <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"Hindi available ang SOS gamit ang satellite sa bansa o rehiyong ito"</string> + <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"Hindi naka-set up ang SOS gamit ang satellite"</string> + <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"Para magpadala ng mensahe sa pamamagitan ng satellite, itakda ang Google Messages bilang iyong default na app sa pagmemensahe"</string> + <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"Hindi available ang SOS gamit ang satellite"</string> + <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"Para tingnan kung available ang SOS gamit ang satellite sa bansa o rehiyong ito, i-on ang mga setting ng lokasyon"</string> + <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"Available ang satellite messaging"</string> + <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"Puwede kang magpadala ng mensahe sa pamamagitan ng satellite kung walang mobile o Wi-Fi network. Dapat Google Messages ang default mong app sa pagmemensahe."</string> + <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"Hindi sinusuportahan ang satellite messaging"</string> + <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"Hindi sinusuportahan ang satellite messaging sa device na ito"</string> + <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"Hindi naka-set up ang satellite messaging"</string> + <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"Tiyaking nakakonekta ka sa internet at subukang mag-set up ulit"</string> + <string name="satellite_messaging_not_in_allowed_region_notification_title" msgid="2035303593479031655">"Hindi available ang satellite messaging"</string> + <string name="satellite_messaging_not_in_allowed_region_notification_summary" msgid="5270294879531815854">"Hindi available ang satellite messaging sa bansa o rehiyong ito"</string> + <string name="satellite_messaging_unsupported_default_sms_app_notification_title" msgid="1004808759472360189">"Hindi naka-set up ang satellite messaging"</string> + <string name="satellite_messaging_unsupported_default_sms_app_notification_summary" msgid="17084124893763593">"Para magpadala ng mensahe sa pamamagitan ng satellite, itakda ang Google Messages bilang iyong default na app sa pagmemensahe"</string> + <string name="satellite_messaging_location_disabled_notification_title" msgid="7270641894250928494">"Hindi available ang satellite messaging"</string> + <string name="satellite_messaging_location_disabled_notification_summary" msgid="1450824950686221810">"Para tingnan kung available ang satellite messaging sa bansa o rehiyong ito, i-on ang mga setting ng lokasyon"</string> <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"I-set up ulit ang Pag-unlock Gamit ang Fingerprint"</string> <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"Hindi na makilala ang <xliff:g id="FINGERPRINT">%s</xliff:g>."</string> <string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"Hindi na makilala ang <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> at <xliff:g id="FINGERPRINT_1">%2$s</xliff:g>."</string> @@ -2497,7 +2477,7 @@ <string name="keyboard_shortcut_group_applications_email" msgid="4229037666415353683">"Email"</string> <string name="keyboard_shortcut_group_applications_sms" msgid="3523799286376321137">"SMS"</string> <string name="keyboard_shortcut_group_applications_music" msgid="2051507523525651067">"Musika"</string> - <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"Kalendaryo"</string> + <string name="keyboard_shortcut_group_applications_calendar" msgid="3571770335653387606">"Calendar"</string> <string name="keyboard_shortcut_group_applications_calculator" msgid="6753209559716091507">"Calculator"</string> <string name="keyboard_shortcut_group_applications_maps" msgid="7950000659522589471">"Mga Mapa"</string> <string name="keyboard_shortcut_group_applications" msgid="3010389163951364798">"Mga Application"</string> diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml index 39a005f628f5..e54b8999a1dc 100644 --- a/core/res/res/values-tr/strings.xml +++ b/core/res/res/values-tr/strings.xml @@ -612,6 +612,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Uygulamanın, yakındaki Ultra Geniş Bant cihazların birbirine göre konumunu belirlemesine izin verin"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"yakındaki kablosuz cihazlarla etkileşim kur"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Uygulamanın reklam sunmasına, bağlanmasına ve yakındaki kablosuz cihazların göreli konumunu belirlemesine izin verir"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Tercih Edilen NFC Ödeme Hizmeti Bilgileri"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Uygulamaya, kayıtlı yardımlar ve rota hedefi gibi tercih edilen NFC ödeme hizmeti bilgilerini alma izni verir."</string> <string name="permlab_nfc" msgid="1904455246837674977">"Yakın Alan İletişimini denetle"</string> diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml index 3b9cd192f7a4..344cb28afcb2 100644 --- a/core/res/res/values-uk/strings.xml +++ b/core/res/res/values-uk/strings.xml @@ -614,6 +614,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"З цим дозволом додаток може визначати відстань між розташованими поблизу пристроями з надширокосмуговим зв’язком"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"взаємодіяти з пристроями Wi‑Fi поблизу"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Додаток може виявляти пристрої Wi‑Fi поблизу, підключатися до них і визначати їх відносне розташування"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Використання інформації з платіжного NFC-сервісу"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Дозволяє додатку отримувати доступ до інформації потрібного платіжного NFC-сервісу (наприклад, пов\'язаних ідентифікаторів чи даних про маршрутизацію трансакцій)."</string> <string name="permlab_nfc" msgid="1904455246837674977">"контрол. Near Field Communication"</string> @@ -1575,7 +1579,7 @@ <string name="sync_really_delete" msgid="5657871730315579051">"Видалити елементи"</string> <string name="sync_undo_deletes" msgid="5786033331266418896">"Відмінити видалення"</string> <string name="sync_do_nothing" msgid="4528734662446469646">"Наразі нічого не робити"</string> - <string name="choose_account_label" msgid="5557833752759831548">"Вибрати обліковий запис"</string> + <string name="choose_account_label" msgid="5557833752759831548">"Вибрати облік. запис"</string> <string name="add_account_label" msgid="4067610644298737417">"Додати обліковий запис"</string> <string name="add_account_button_label" msgid="322390749416414097">"Додати облік. запис"</string> <string name="number_picker_increment_button" msgid="7621013714795186298">"Збільшити"</string> diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml index 679f1026197a..45e1a2347dbb 100644 --- a/core/res/res/values-ur/strings.xml +++ b/core/res/res/values-ur/strings.xml @@ -612,6 +612,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"ایپ کو قریبی الٹرا وائڈ بینڈ آلات کے مابین متعلقہ پوزیشن کا تعین کرنے کی اجازت دیں"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"قریبی Wi-Fi آلات کے ساتھ تعامل کریں"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"ایپ کو اشتہار دینے، منسلک کرنے اور قریبی Wi-Fi آلات کی متعلقہ پوزیشن کا تعین کرنے کی اجازت دیتا ہے"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"ترجیح شدہ NFC ادائیگی کی سروس کی معلومات"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"ایپ کو رجسٹرشدہ ایڈز اور روٹ ڈسٹنیشن جیسی ترجیح شدہ nfc ادائیگی سروس کی معلومات حاصل کرنے کی اجازت دیتا ہے۔"</string> <string name="permlab_nfc" msgid="1904455246837674977">"Near Field کمیونیکیشن کنٹرول کریں"</string> @@ -2431,54 +2435,30 @@ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"آن کریں"</string> <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"واپس جائیں"</string> <string name="unarchival_session_app_label" msgid="6811856981546348205">"زیر التواء..."</string> - <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) --> - <skip /> - <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) --> - <skip /> + <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"سیٹلائٹ SOS اب دستیاب ہے"</string> + <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"موبائل یا Wi-Fi نیٹ ورک نہ ہونے پر آپ ایمرجنسی سروسز کو پیغام بھیج سکتے ہیں۔ Google پیغامات آپ کی ڈیفالٹ پیغام رسانی ایپ ہونی چاہیے۔"</string> + <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"سیٹلائٹ SOS تعاون یافتہ نہیں ہے"</string> + <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"سیٹلائٹ SOS اس آلے پر تعاون یافتہ نہیں ہے"</string> + <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"سیٹلائٹ SOS سیٹ اپ نہیں ہے"</string> + <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"یقینی بنائیں کہ آپ انٹرنیٹ سے منسلک ہیں اور دوبارہ سیٹ اپ کرنے کی کوشش کریں"</string> + <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"سیٹلائٹ SOS دستیاب نہیں ہے"</string> + <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"سیٹلائٹ SOS اس ملک یا علاقے میں دستیاب نہیں ہے"</string> + <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"سیٹلائٹ SOS سیٹ اپ نہیں ہے"</string> + <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"سیٹلائٹ کے ذریعے پیغام بھیجنے کے لیے، Google پیغامات کو اپنی ڈیفالٹ پیغام رسانی ایپ کے طور پر سیٹ کریں"</string> + <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"سیٹلائٹ SOS دستیاب نہیں ہے"</string> + <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"یہ چیک کرنے کے لیے کہ آیا اس ملک یا علاقے میں سیٹلائٹ SOS دستیاب ہے، مقام کی ترتیبات کو آن کریں"</string> + <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"سیٹلائٹ پیغام رسانی دستیاب ہے"</string> + <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"موبائل یا Wi-Fi نیٹ ورک نہ ہونے پر آپ سیٹلائٹ کے ذریعے پیغام بھیج سکتے ہیں۔ Google پیغامات آپ کی ڈیفالٹ پیغام رسانی ایپ ہونی چاہیے۔"</string> + <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"سیٹلائٹ پیغام رسانی تعاون یافتہ نہیں ہے"</string> + <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"سیٹلائٹ پیغام رسانی اس آلے پر تعاون یافتہ نہیں ہے"</string> + <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"سیٹلائٹ پیغام رسانی سیٹ اپ نہیں ہے"</string> + <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"یقینی بنائیں کہ آپ انٹرنیٹ سے منسلک ہیں اور دوبارہ سیٹ اپ کرنے کی کوشش کریں"</string> + <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 پیغامات کو اپنی ڈیفالٹ پیغام رسانی ایپ کے طور پر سیٹ کریں"</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> <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-uz/strings.xml b/core/res/res/values-uz/strings.xml index d7620e1f3afb..f123bea572b1 100644 --- a/core/res/res/values-uz/strings.xml +++ b/core/res/res/values-uz/strings.xml @@ -612,6 +612,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Ilovaga yaqin atrofdagi ultra keng polosali qurilmalarining nisbiy joylashishini aniqlashga ruxsat beradi"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"Yaqin-atrofdagi Wi-Fi qurilmalar bilan ishlash"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Ilovaga yaqin-atrofdagi Wi-Fi qurilmalarga reklama yuborish, ulanish va ularning taxminiy joylashuvini aniqlash imkonini beradi."</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Asosiy NFC toʻlov xizmati haqidagi axborot"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Bu ilovaga asosiy NFC toʻlov xizmati haqidagi axborotni olish imkonini beradi (masalan, qayd qilingan AID identifikatorlari va marshrutning yakuniy manzili)."</string> <string name="permlab_nfc" msgid="1904455246837674977">"NFC modulini boshqarish"</string> @@ -2431,54 +2435,30 @@ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"Yoqish"</string> <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"Orqaga"</string> <string name="unarchival_session_app_label" msgid="6811856981546348205">"Kutilmoqda..."</string> - <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) --> - <skip /> - <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) --> - <skip /> + <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"Sputnik SOS xizmati mavjud"</string> + <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"Agar mobil yoki Wi-Fi tarmoq boʻlmasa, favqulodda xizmatlarga xabar yuborishingiz mumkin. Google Xabarlar asosiy xabar almashinuv ilovasi boʻlishi zarur."</string> + <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"Sputnik SOS ishlamaydi"</string> + <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"Bu qurilmada sputnik SOS ishlamaydi"</string> + <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"Sputnik SOS sozlanmagan"</string> + <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"Internetga ulanganingizni tekshiring va qayta sozlang"</string> + <string name="satellite_sos_not_in_allowed_region_notification_title" msgid="3164093193467075926">"Sputnik SOS mavjud emas"</string> + <string name="satellite_sos_not_in_allowed_region_notification_summary" msgid="7686947667515679672">"Sputnik SOS bu mamlakat yoki hududda mavjud emas"</string> + <string name="satellite_sos_unsupported_default_sms_app_notification_title" msgid="292528603128702080">"Sputnik SOS sozlanmagan"</string> + <string name="satellite_sos_unsupported_default_sms_app_notification_summary" msgid="3165168393504548437">"Sunʼiy yoʻldosh orqali xabarlashish uchun Google Xabarlar ilovasini asosiy xabar almashinuv ilovasi sifatida sozlang"</string> + <string name="satellite_sos_location_disabled_notification_title" msgid="5427987916850950591">"Sputnik SOS mavjud emas"</string> + <string name="satellite_sos_location_disabled_notification_summary" msgid="1544937460641058567">"Bu mamlakat yoki hududda sputnik SOS mavjudligini tekshirish uchun joylashuv sozlamalarini yoqing"</string> + <string name="satellite_messaging_available_notification_title" msgid="3366657987618784706">"Sunʼiy yoʻldosh orqali xabarlashuv mavjud"</string> + <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"Agar mobil yoki Wi-Fi tarmoq boʻlmasa, sunʼiy yoʻldosh orqali xabar yuborishingiz mumkin. Google Xabarlar asosiy xabar almashinuv ilovasi boʻlishi zarur."</string> + <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"Sunʼiy yoʻldosh orqali xabarlashuv ishlamaydi"</string> + <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"Bu qurilmada sunʼiy yoʻldosh orqali xabarlashuv ishlamaydi"</string> + <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"Sunʼiy yoʻldosh orqali xabarlashuv sozlanmagan"</string> + <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"Internetga ulanganingizni tekshiring va qayta sozlang"</string> + <string name="satellite_messaging_not_in_allowed_region_notification_title" msgid="2035303593479031655">"Sunʼiy yoʻldosh orqali xabarlashuv mavjud emas"</string> + <string name="satellite_messaging_not_in_allowed_region_notification_summary" msgid="5270294879531815854">"Sunʼiy yoʻldosh orqali xabarlashuv ushbu mamlakat yoki hududda ishlamaydi"</string> + <string name="satellite_messaging_unsupported_default_sms_app_notification_title" msgid="1004808759472360189">"Sunʼiy yoʻldosh orqali xabarlashuv sozlanmagan"</string> + <string name="satellite_messaging_unsupported_default_sms_app_notification_summary" msgid="17084124893763593">"Sunʼiy yoʻldosh orqali xabarlashish uchun Google Xabarlar ilovasini asosiy xabar almashinuv ilovasi sifatida sozlang"</string> + <string name="satellite_messaging_location_disabled_notification_title" msgid="7270641894250928494">"Sunʼiy yoʻldosh orqali xabarlashuv mavjud emas"</string> + <string name="satellite_messaging_location_disabled_notification_summary" msgid="1450824950686221810">"Bu mamlakat yoki hududda sunʼiy yoʻldosh orqali xabarlashuv mavjudligini tekshirish uchun joylashuv sozlamalarini yoqing"</string> <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Barmoq izi bilan ochish funksiyasini qayta sozlang"</string> <string name="fingerprint_dangling_notification_msg_1" msgid="5851784577768803510">"<xliff:g id="FINGERPRINT">%s</xliff:g> endi tanilmaydi."</string> <string name="fingerprint_dangling_notification_msg_2" msgid="7925203589860744456">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> va <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> endi tanilmaydi."</string> diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml index 663078b5caf4..ef50a9badc4c 100644 --- a/core/res/res/values-vi/strings.xml +++ b/core/res/res/values-vi/strings.xml @@ -612,6 +612,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Cho phép ứng dụng xác định khoảng cách tương đối giữa các thiết bị ở gần dùng Băng tần siêu rộng"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"tương tác với các thiết bị Wi‑Fi lân cận"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Cho phép ứng dụng này thông báo, kết nối và xác định vị trí tương đối của các thiết bị Wi‑Fi lân cận"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Thông tin về dịch vụ thanh toán qua công nghệ giao tiếp tầm gần (NFC) được ưu tiên"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Cho phép ứng dụng nhận thông tin về dịch vụ thanh toán qua công nghệ giao tiếp tầm gần mà bạn ưu tiên, chẳng hạn như các hình thức hỗ trợ đã đăng ký và điểm đến trong hành trình."</string> <string name="permlab_nfc" msgid="1904455246837674977">"kiểm soát Liên lạc trường gần"</string> diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml index 6007d1e5f9c5..e2f66fc93c08 100644 --- a/core/res/res/values-zh-rCN/strings.xml +++ b/core/res/res/values-zh-rCN/strings.xml @@ -612,6 +612,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"允许应用确定附近超宽带设备之间的相对位置"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"与附近的 WLAN 设备互动"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"允许该应用向附近的 WLAN 设备进行广播、连接到这些设备以及确定这些设备的相对位置"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"首选 NFC 付款服务信息"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"允许应用获取首选 NFC 付款服务信息,例如注册的应用标识符和路线目的地。"</string> <string name="permlab_nfc" msgid="1904455246837674977">"控制近距离通信"</string> @@ -2431,54 +2435,30 @@ <string name="satellite_manual_selection_state_popup_ok" msgid="2459664752624985095">"开启"</string> <string name="satellite_manual_selection_state_popup_cancel" msgid="973605633339469252">"返回"</string> <string name="unarchival_session_app_label" msgid="6811856981546348205">"待归档…"</string> - <!-- no translation found for satellite_sos_available_notification_title (5396708154268096124) --> - <skip /> - <!-- no translation found for satellite_sos_available_notification_summary (1727088812951848330) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_title (2659100983227637285) --> - <skip /> - <!-- no translation found for satellite_sos_not_supported_notification_summary (1071762454665310549) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_title (8564738683795406715) --> - <skip /> - <!-- no translation found for satellite_sos_not_provisioned_notification_summary (3127320958911180629) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_title (3164093193467075926) --> - <skip /> - <!-- no translation found for satellite_sos_not_in_allowed_region_notification_summary (7686947667515679672) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_title (292528603128702080) --> - <skip /> - <!-- no translation found for satellite_sos_unsupported_default_sms_app_notification_summary (3165168393504548437) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_title (5427987916850950591) --> - <skip /> - <!-- no translation found for satellite_sos_location_disabled_notification_summary (1544937460641058567) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_title (3366657987618784706) --> - <skip /> - <!-- no translation found for satellite_messaging_available_notification_summary (7573949038500243670) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_title (8202139632766878610) --> - <skip /> - <!-- no translation found for satellite_messaging_not_supported_notification_summary (61629858627638545) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_title (961909101918169727) --> - <skip /> - <!-- no translation found for satellite_messaging_not_provisioned_notification_summary (1060961852174442155) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_title (2035303593479031655) --> - <skip /> - <!-- no translation found for satellite_messaging_not_in_allowed_region_notification_summary (5270294879531815854) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_title (1004808759472360189) --> - <skip /> - <!-- no translation found for satellite_messaging_unsupported_default_sms_app_notification_summary (17084124893763593) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_title (7270641894250928494) --> - <skip /> - <!-- no translation found for satellite_messaging_location_disabled_notification_summary (1450824950686221810) --> - <skip /> + <string name="satellite_sos_available_notification_title" msgid="5396708154268096124">"现在支持卫星紧急呼救功能"</string> + <string name="satellite_sos_available_notification_summary" msgid="1727088812951848330">"即使没有移动网络或 WLAN 网络,您仍可以向应急服务发送消息。您必须将 Google 信息设为默认的即时通讯应用。"</string> + <string name="satellite_sos_not_supported_notification_title" msgid="2659100983227637285">"不支持卫星紧急呼救功能"</string> + <string name="satellite_sos_not_supported_notification_summary" msgid="1071762454665310549">"此设备不支持卫星紧急呼救功能"</string> + <string name="satellite_sos_not_provisioned_notification_title" msgid="8564738683795406715">"未设置卫星紧急呼救功能"</string> + <string name="satellite_sos_not_provisioned_notification_summary" msgid="3127320958911180629">"请确保您已联网,然后尝试重新设置"</string> + <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 信息设为默认即时通讯应用"</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> + <string name="satellite_messaging_available_notification_summary" msgid="7573949038500243670">"即使没有移动网络或 WLAN 网络,您仍可以通过卫星发送消息。您必须将 Google 信息设为默认的即时通讯应用。"</string> + <string name="satellite_messaging_not_supported_notification_title" msgid="8202139632766878610">"不支持卫星消息功能"</string> + <string name="satellite_messaging_not_supported_notification_summary" msgid="61629858627638545">"此设备不支持卫星消息功能"</string> + <string name="satellite_messaging_not_provisioned_notification_title" msgid="961909101918169727">"未设置卫星消息功能"</string> + <string name="satellite_messaging_not_provisioned_notification_summary" msgid="1060961852174442155">"请确保您已联网,然后尝试重新设置"</string> + <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 信息设为默认即时通讯应用"</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> <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-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml index fcf50e4beec4..a2d32805df76 100644 --- a/core/res/res/values-zh-rHK/strings.xml +++ b/core/res/res/values-zh-rHK/strings.xml @@ -612,6 +612,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"允許應用程式判斷附近超寬頻裝置之間的相對位置"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"與附近的 Wi‑Fi 裝置互動"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"允許應用程式向附近的 Wi-Fi 裝置顯示此裝置、連接這些裝置並判斷其相對位置"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"由用戶允許授權的 NFC 付款服務資訊"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"允許應用程式取得由用戶允許授權的 NFC 付款服務資訊 (如已註冊的付款輔助功能和最終付款對象)。"</string> <string name="permlab_nfc" msgid="1904455246837674977">"控制近距離無線通訊"</string> diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml index e186e7c908b2..d1f8b5a289ed 100644 --- a/core/res/res/values-zh-rTW/strings.xml +++ b/core/res/res/values-zh-rTW/strings.xml @@ -612,6 +612,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"允許應用程式判斷附近超寬頻裝置間的相對位置"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"與鄰近的 Wi-Fi 裝置互動"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"允許應用程式顯示鄰近的 Wi-Fi 裝置的資料、與其連線並判斷相對位置"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"首選 NFC 付費服務資訊"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"允許應用程式取得首選 NFC 付費服務資訊,例如已註冊的輔助工具和路線目的地。"</string> <string name="permlab_nfc" msgid="1904455246837674977">"控制近距離無線通訊"</string> @@ -640,12 +644,12 @@ <string name="permdesc_imagesWrite" msgid="5195054463269193317">"允許應用程式修改你的相片收藏。"</string> <string name="permlab_mediaLocation" msgid="7368098373378598066">"讀取你的媒體收藏的位置資訊"</string> <string name="permdesc_mediaLocation" msgid="597912899423578138">"允許應用程式讀取你的媒體收藏的位置資訊。"</string> - <string name="biometric_app_setting_name" msgid="3339209978734534457">"使用生物特徵辨識功能"</string> - <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"使用生物特徵辨識或螢幕鎖定功能"</string> + <string name="biometric_app_setting_name" msgid="3339209978734534457">"使用生物辨識功能"</string> + <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"使用生物辨識或螢幕鎖定功能"</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"驗證你的身分"</string> - <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"如要繼續操作,請使用生物特徵辨識功能驗證身分"</string> - <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"請使用生物特徵辨識或螢幕鎖定功能驗證身分,才能繼續操作"</string> - <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"無法使用生物特徵辨識硬體"</string> + <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"如要繼續操作,請使用生物辨識功能驗證身分"</string> + <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"請使用生物辨識或螢幕鎖定功能驗證身分,才能繼續操作"</string> + <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"無法使用生物辨識硬體"</string> <string name="biometric_error_user_canceled" msgid="6732303949695293730">"已取消驗證"</string> <string name="biometric_not_recognized" msgid="5106687642694635888">"無法辨識"</string> <string name="biometric_face_not_recognized" msgid="5535599455744525200">"無法辨識臉孔"</string> diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml index 69ec5edc9004..d1b7e510168e 100644 --- a/core/res/res/values-zu/strings.xml +++ b/core/res/res/values-zu/strings.xml @@ -612,6 +612,10 @@ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Vumela i-app inqume indawo ehambelanayo phakathi kwamadivayisi e-Ultra-Wideband aseduze"</string> <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"xhumana namadivayisi we-Wi‑Fi aseduze"</string> <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Ivumela i-app ikhangise, ixhume, futhi inqume isimo esihambisanayo samadivayisi we-Wi-Fi aseduze"</string> + <!-- no translation found for permlab_ranging (2854543350668593390) --> + <skip /> + <!-- no translation found for permdesc_ranging (6703905535621521710) --> + <skip /> <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Ulwazi Lwesevisi Yenkokhelo Ye-NFC Okhethwayo"</string> <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Ivuemela uhlelo lokusebenza ukuthola ulwazi lesevisi yenkokhelo ye-nfc njengezinsiza zokubhalisa nezindawo zomzila."</string> <string name="permlab_nfc" msgid="1904455246837674977">"lawula Uxhumano Lwenkambu Eseduze"</string> diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index 4d73f228ad0c..41dec3776b5c 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -2273,6 +2273,22 @@ <attr name="enableOnBackInvokedCallback" format="boolean"/> <attr name="intentMatchingFlags"/> + + <!-- Specifies the set of drawable resources that can be used in place + of an existing declared icon or banner for activities that appear + in the app launcher. The resource referenced must be an array of + drawable resources and can contain at most 500 items. + {@link android.content.pm.PackageManager#changeLauncherIconConfig} + @FlaggedApi(android.content.pm.Flags.FLAG_CHANGE_LAUNCHER_BADGING) --> + <attr name="alternateLauncherIcons" format="reference" /> + + <!-- Specifies the set of string resources that can be used in place + of an existing declared label for activities that appear + in the app launcher. The resource referenced must be an array of + string resources and can contain at most 500 items. + {@link android.content.pm.PackageManager#changeLauncherIconConfig} + @FlaggedApi(android.content.pm.Flags.FLAG_CHANGE_LAUNCHER_BADGING) --> + <attr name="alternateLauncherLabels" format="reference" /> </declare-styleable> <!-- An attribution is a logical part of an app and is identified by a tag. diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 7402a2f24f97..969ee2e16deb 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1849,6 +1849,10 @@ <item>-1</item> </integer-array> + <!-- Specifies the delay in milliseconds after the last user input before turning off the + keyboard backlight. + --> + <integer name="config_keyboardBacklightTimeoutMs">30000</integer> <!-- An array describing the screen's backlight values corresponding to the brightness values in the config_screenBrightnessNits array. @@ -4596,6 +4600,11 @@ exists on the device, the accessibility shortcut will be disabled by default. --> <string name="config_defaultAccessibilityService" translatable="false"></string> + <!-- The component name, flattened to a string, for the default select to speak service to be + enabled by the accessibility keyboard shortcut. If the service with the specified component + name is not preinstalled then this shortcut will do nothing. --> + <string name="config_defaultSelectToSpeakService" translatable="false"></string> + <!-- URI for default Accessibility notification sound when to enable accessibility shortcut. --> <string name="config_defaultAccessibilityNotificationSound" translatable="false"></string> @@ -7000,6 +7009,15 @@ <string-array name="config_healthConnectMigrationKnownSigners"> </string-array> + <!-- Certificate digests for trusted apps that will be allowed to obtain the knownSigner + permission for backing up HealthConnect's data and settings. The digest should be computed over the + DER encoding of the trusted certificate using the SHA-256 digest algorithm. --> + <string-array name="config_backupHealthConnectDataAndSettingsKnownSigners"/> + <!-- Certificate digests for trusted apps that will be allowed to obtain the knownSigner + permission for restoring HealthConnect's data and settings. The digest should be computed over the + DER encoding of the trusted certificate using the SHA-256 digest algorithm. --> + <string-array name="config_restoreHealthConnectDataAndSettingsKnownSigners"/> + <!-- Package name of Health Connect data migrator application. --> <string name="config_healthConnectMigratorPackageName"></string> diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml index 70cc5f14391d..b6436d0b30a5 100644 --- a/core/res/res/values/public-staging.xml +++ b/core/res/res/values/public-staging.xml @@ -127,6 +127,10 @@ <public name="intentMatchingFlags"/> <!-- @FlaggedApi(android.view.inputmethod.Flags.FLAG_IME_SWITCHER_REVAMP_API) --> <public name="layoutLabel"/> + <!-- @FlaggedApi(android.content.pm.Flags.FLAG_CHANGE_LAUNCHER_BADGING) --> + <public name="alternateLauncherIcons"/> + <!-- @FlaggedApi(android.content.pm.Flags.FLAG_CHANGE_LAUNCHER_BADGING) --> + <public name="alternateLauncherLabels"/> </staging-public-group> <staging-public-group type="id" first-id="0x01b60000"> diff --git a/core/res/res/values/stoppable_fgs_system_apps.xml b/core/res/res/values/stoppable_fgs_system_apps.xml index 165ff61c7b3e..06843f4b4f76 100644 --- a/core/res/res/values/stoppable_fgs_system_apps.xml +++ b/core/res/res/values/stoppable_fgs_system_apps.xml @@ -19,6 +19,7 @@ <resources> <!-- A list of system apps whose FGS can be stopped in the task manager. --> <string-array translatable="false" name="stoppable_fgs_system_apps"> + <item>com.android.virtualization.terminal</item> </string-array> <!-- stoppable_fgs_system_apps which is supposed to be overridden by vendor --> <string-array translatable="false" name="vendor_stoppable_fgs_system_apps"> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index db81a3be440f..9dd302784c2c 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2098,6 +2098,7 @@ <java-symbol type="integer" name="config_autoBrightnessDarkeningLightDebounce"/> <java-symbol type="integer" name="config_autoBrightnessInitialLightSensorRate"/> <java-symbol type="integer" name="config_autoBrightnessLightSensorRate"/> + <java-symbol type="integer" name="config_keyboardBacklightTimeoutMs" /> <java-symbol type="integer" name="config_carDockKeepsScreenOn" /> <java-symbol type="integer" name="config_criticalBatteryWarningLevel" /> <java-symbol type="integer" name="config_datause_notification_type" /> @@ -3718,6 +3719,7 @@ <java-symbol type="string" name="color_correction_feature_name" /> <java-symbol type="string" name="reduce_bright_colors_feature_name" /> <java-symbol type="string" name="config_defaultAccessibilityService" /> + <java-symbol type="string" name="config_defaultSelectToSpeakService" /> <java-symbol type="string" name="config_defaultAccessibilityNotificationSound" /> <java-symbol type="string" name="accessibility_shortcut_spoken_feedback" /> <java-symbol type="array" name="config_trustedAccessibilityServices" /> diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp index a382d798fb9b..f39508d6de15 100644 --- a/core/tests/coretests/Android.bp +++ b/core/tests/coretests/Android.bp @@ -151,6 +151,7 @@ android_test { ":HelloWorldUsingSdk1And2", ":HelloWorldUsingSdkMalformedNegativeVersion", ":CtsStaticSharedLibConsumerApp1", + ":CtsStaticSharedLibConsumerApp3", ], } diff --git a/core/tests/coretests/AndroidTest.xml b/core/tests/coretests/AndroidTest.xml index 3f7c83a82787..5d8ff87eca24 100644 --- a/core/tests/coretests/AndroidTest.xml +++ b/core/tests/coretests/AndroidTest.xml @@ -41,6 +41,8 @@ value="/data/local/tmp/tests/coretests/pm/HelloWorldSdk1.apk"/> <option name="push-file" key="CtsStaticSharedLibConsumerApp1.apk" value="/data/local/tmp/tests/coretests/pm/CtsStaticSharedLibConsumerApp1.apk"/> + <option name="push-file" key="CtsStaticSharedLibConsumerApp3.apk" + value="/data/local/tmp/tests/coretests/pm/CtsStaticSharedLibConsumerApp3.apk"/> </target_preparer> <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> diff --git a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java index da1fffa0fac4..a2598f69e031 100644 --- a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java +++ b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java @@ -16,6 +16,7 @@ package android.app; +import static android.app.Flags.FLAG_PIC_ISOLATE_CACHE_BY_UID; import static android.app.PropertyInvalidatedCache.NONCE_UNSET; import static android.app.PropertyInvalidatedCache.MODULE_BLUETOOTH; import static android.app.PropertyInvalidatedCache.MODULE_SYSTEM; @@ -30,8 +31,9 @@ import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import android.app.PropertyInvalidatedCache.Args; import android.annotation.SuppressLint; +import android.app.PropertyInvalidatedCache.Args; +import android.os.Binder; import com.android.internal.os.ApplicationSharedMemory; import android.platform.test.annotations.IgnoreUnderRavenwood; @@ -58,6 +60,7 @@ import org.junit.Test; */ @SmallTest public class PropertyInvalidatedCacheTests { + @Rule public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); @@ -455,8 +458,9 @@ public class PropertyInvalidatedCacheTests { // Test the Args-style constructor. @Test public void testArgsConstructor() { - // Create a cache with a maximum of four entries. - TestCache cache = new TestCache(new Args(MODULE_TEST).api("init1").maxEntries(4), + // Create a cache with a maximum of four entries and non-isolated UIDs. + TestCache cache = new TestCache(new Args(MODULE_TEST) + .maxEntries(4).isolateUids(false).api("init1"), new TestQuery()); cache.invalidateCache(); @@ -570,4 +574,73 @@ public class PropertyInvalidatedCacheTests { // Expected exception. } } + + // Verify that a cache created with isolatedUids(true) separates out the results. + @RequiresFlagsEnabled(FLAG_PIC_ISOLATE_CACHE_BY_UID) + @Test + public void testIsolatedUids() { + TestCache cache = new TestCache(new Args(MODULE_TEST) + .maxEntries(4).isolateUids(true).api("testIsolatedUids").testMode(true), + new TestQuery()); + cache.invalidateCache(); + final int uid1 = 1; + final int uid2 = 2; + + long token = Binder.setCallingWorkSourceUid(uid1); + try { + // Populate the cache for user 1 + assertEquals("foo5", cache.query(5)); + assertEquals(1, cache.getRecomputeCount()); + assertEquals("foo5", cache.query(5)); + assertEquals(1, cache.getRecomputeCount()); + assertEquals("foo6", cache.query(6)); + assertEquals(2, cache.getRecomputeCount()); + + // Populate the cache for user 2. User 1 values are not reused. + Binder.setCallingWorkSourceUid(uid2); + assertEquals("foo5", cache.query(5)); + assertEquals(3, cache.getRecomputeCount()); + assertEquals("foo5", cache.query(5)); + assertEquals(3, cache.getRecomputeCount()); + + // Verify that the cache for user 1 is still populated. + Binder.setCallingWorkSourceUid(uid1); + assertEquals("foo5", cache.query(5)); + assertEquals(3, cache.getRecomputeCount()); + + } finally { + Binder.restoreCallingWorkSource(token); + } + + // Repeat the test with a non-isolated cache. + cache = new TestCache(new Args(MODULE_TEST) + .maxEntries(4).isolateUids(false).api("testIsolatedUids2").testMode(true), + new TestQuery()); + cache.invalidateCache(); + token = Binder.setCallingWorkSourceUid(uid1); + try { + // Populate the cache for user 1 + assertEquals("foo5", cache.query(5)); + assertEquals(1, cache.getRecomputeCount()); + assertEquals("foo5", cache.query(5)); + assertEquals(1, cache.getRecomputeCount()); + assertEquals("foo6", cache.query(6)); + assertEquals(2, cache.getRecomputeCount()); + + // Populate the cache for user 2. User 1 values are reused. + Binder.setCallingWorkSourceUid(uid2); + assertEquals("foo5", cache.query(5)); + assertEquals(2, cache.getRecomputeCount()); + assertEquals("foo5", cache.query(5)); + assertEquals(2, cache.getRecomputeCount()); + + // Verify that the cache for user 1 is still populated. + Binder.setCallingWorkSourceUid(uid1); + assertEquals("foo5", cache.query(5)); + assertEquals(2, cache.getRecomputeCount()); + + } finally { + Binder.restoreCallingWorkSource(token); + } + } } diff --git a/core/tests/coretests/src/android/content/IntentTest.java b/core/tests/coretests/src/android/content/IntentTest.java index d169ce3c07d0..7bc4abd935b6 100644 --- a/core/tests/coretests/src/android/content/IntentTest.java +++ b/core/tests/coretests/src/android/content/IntentTest.java @@ -37,6 +37,10 @@ import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + /** * Build/Install/Run: * atest FrameworksCoreTests:IntentTest @@ -57,7 +61,12 @@ public class IntentTest { public void testReadFromParcelWithExtraIntentKeys() { Intent intent = new Intent("TEST_ACTION"); intent.putExtra(TEST_EXTRA_NAME, new Intent(TEST_ACTION)); + // Not an intent, don't count. intent.putExtra(TEST_EXTRA_NAME + "2", 1); + ArrayList<Intent> intents = new ArrayList<>(); + intents.add(new Intent(TEST_ACTION)); + intent.putParcelableArrayListExtra(TEST_EXTRA_NAME + "3", intents); + intent.setClipData(ClipData.newIntent("label", new Intent(TEST_ACTION))); intent.collectExtraIntentKeys(); final Parcel parcel = Parcel.obtain(); @@ -68,7 +77,7 @@ public class IntentTest { assertEquals(intent.getAction(), target.getAction()); assertEquals(intent.getExtraIntentKeys(), target.getExtraIntentKeys()); - assertThat(intent.getExtraIntentKeys()).hasSize(1); + assertThat(intent.getExtraIntentKeys()).hasSize(3); } @Test @@ -87,13 +96,37 @@ public class IntentTest { @RequiresFlagsEnabled(Flags.FLAG_PREVENT_INTENT_REDIRECT) public void testCollectExtraIntentKeys() { Intent intent = new Intent(TEST_ACTION); - Intent extraIntent = new Intent(TEST_ACTION, TEST_URI); - intent.putExtra(TEST_EXTRA_NAME, extraIntent); + + Intent[] intents = new Intent[10]; + for (int i = 0; i < intents.length; i++) { + intents[i] = new Intent("action" + i); + } + Intent[] intents2 = new Intent[2]; // intents[6-7] + System.arraycopy(intents, 6, intents2, 0, intents2.length); + ArrayList<Intent> intents3 = new ArrayList<>(2); + intents3.addAll(Arrays.asList(intents).subList(8, 10)); // intents[8-9] + intent.putExtra("key1", intents[0]); + intent.putExtra("array-key", intents2); + intent.setClipData(ClipData.newIntent("label2", intents[1])); + intent.putExtra("intkey", 1); + intents[0].putExtra("key3", intents[2]); + intents[0].setClipData(ClipData.newIntent("label4", intents[3])); + intents[0].putParcelableArrayListExtra("array-list-key", intents3); + intents[1].putExtra("key3", intents[4]); + intents[1].setClipData(ClipData.newIntent("label4", intents[5])); + intents[5].putExtra("intkey", 2); intent.collectExtraIntentKeys(); - assertThat(intent.getExtraIntentKeys()).hasSize(1); - assertThat(intent.getExtraIntentKeys()).contains(TEST_EXTRA_NAME); + // collect all actions of nested intents. + final List<String> actions = new ArrayList<>(); + intent.forEachNestedCreatorToken(intent1 -> { + actions.add(intent1.getAction()); + }); + assertThat(actions).hasSize(10); + for (int i = 0; i < intents.length; i++) { + assertThat(actions).contains("action" + i); + } } } diff --git a/core/tests/coretests/src/android/content/pm/parsing/ApkLiteParseUtilsTest.java b/core/tests/coretests/src/android/content/pm/parsing/ApkLiteParseUtilsTest.java index d4618d744644..0db49a72c51d 100644 --- a/core/tests/coretests/src/android/content/pm/parsing/ApkLiteParseUtilsTest.java +++ b/core/tests/coretests/src/android/content/pm/parsing/ApkLiteParseUtilsTest.java @@ -72,6 +72,12 @@ public class ApkLiteParseUtilsTest { private static final String TEST_APP_USING_SDK_MALFORMED_VERSION = "HelloWorldUsingSdkMalformedNegativeVersion.apk"; private static final String TEST_APP_USING_STATIC_LIB = "CtsStaticSharedLibConsumerApp1.apk"; + private static final String TEST_APP_USING_STATIC_LIB_TWO_CERTS = + "CtsStaticSharedLibConsumerApp3.apk"; + private static final String STATIC_LIB_CERT_1 = + "70fbd440503ec0bf41f3f21fcc83ffd39880133c27deb0945ed677c6f31d72fb"; + private static final String STATIC_LIB_CERT_2 = + "e49582ff3a0aa4c5589fc5feaac6b7d6e757199dd0c6742df7bf37c2ffef95f5"; private static final String TEST_SDK1 = "HelloWorldSdk1.apk"; private static final String TEST_SDK1_PACKAGE = "com.test.sdk1_1"; private static final String TEST_SDK1_NAME = "com.test.sdk1"; @@ -86,7 +92,7 @@ public class ApkLiteParseUtilsTest { @Before public void setUp() throws IOException { - mTmpDir = mTemporaryFolder.newFolder("DexMetadataHelperTest"); + mTmpDir = mTemporaryFolder.newFolder("ApkLiteParseUtilsTest"); } @After @@ -108,9 +114,8 @@ public class ApkLiteParseUtilsTest { assertThat(baseApk.getUsesSdkLibrariesVersionsMajor()).asList().containsExactly( TEST_SDK1_VERSION, TEST_SDK2_VERSION ); - for (String[] certDigests: baseApk.getUsesSdkLibrariesCertDigests()) { - assertThat(certDigests).asList().containsExactly(""); - } + String[][] expectedCerts = {{""}, {""}}; + assertThat(baseApk.getUsesSdkLibrariesCertDigests()).isEqualTo(expectedCerts); } @SuppressLint("CheckResult") @@ -126,18 +131,13 @@ public class ApkLiteParseUtilsTest { ApkLite baseApk = result.getResult(); String[][] liteCerts = baseApk.getUsesSdkLibrariesCertDigests(); - assertThat(liteCerts).isNotNull(); - for (String[] certDigests: liteCerts) { - assertThat(certDigests).asList().containsExactly(certDigest); - } + String[][] expectedCerts = {{certDigest}, {certDigest}}; + assertThat(liteCerts).isEqualTo(expectedCerts); // Same for package parser AndroidPackage pkg = mPackageParser2.parsePackage(apkFile, 0, true).hideAsFinal(); String[][] pkgCerts = pkg.getUsesSdkLibrariesCertDigests(); - assertThat(pkgCerts).isNotNull(); - for (int i = 0; i < liteCerts.length; i++) { - assertThat(liteCerts[i]).isEqualTo(pkgCerts[i]); - } + assertThat(liteCerts).isEqualTo(pkgCerts); } @@ -160,9 +160,7 @@ public class ApkLiteParseUtilsTest { String[][] liteCerts = baseApk.getUsesSdkLibrariesCertDigests(); String[][] pkgCerts = pkg.getUsesSdkLibrariesCertDigests(); - for (int i = 0; i < liteCerts.length; i++) { - assertThat(liteCerts[i]).isEqualTo(pkgCerts[i]); - } + assertThat(liteCerts).isEqualTo(pkgCerts); } @SuppressLint("CheckResult") @@ -184,9 +182,27 @@ public class ApkLiteParseUtilsTest { String[][] liteCerts = baseApk.getUsesStaticLibrariesCertDigests(); String[][] pkgCerts = pkg.getUsesStaticLibrariesCertDigests(); - for (int i = 0; i < liteCerts.length; i++) { - assertThat(liteCerts[i]).isEqualTo(pkgCerts[i]); - } + assertThat(liteCerts).isEqualTo(pkgCerts); + } + + @Test + public void testParseApkLite_getUsesStaticLibrary_twoCerts() + throws Exception { + File apkFile = copyApkToTmpDir(TEST_APP_USING_STATIC_LIB_TWO_CERTS); + ParseResult<ApkLite> result = ApkLiteParseUtils + .parseApkLite(ParseTypeImpl.forDefaultParsing().reset(), apkFile, 0); + assertThat(result.isError()).isFalse(); + ApkLite baseApk = result.getResult(); + + // There are two certs. + String[][] expectedCerts = {{STATIC_LIB_CERT_1, STATIC_LIB_CERT_2}}; + String[][] liteCerts = baseApk.getUsesStaticLibrariesCertDigests(); + assertThat(liteCerts).isEqualTo(expectedCerts); + + // And they are same as package parser. + AndroidPackage pkg = mPackageParser2.parsePackage(apkFile, 0, true).hideAsFinal(); + String[][] pkgCerts = pkg.getUsesStaticLibrariesCertDigests(); + assertThat(liteCerts).isEqualTo(pkgCerts); } @SuppressLint("CheckResult") diff --git a/core/tests/coretests/src/android/hardware/display/DisplayTopologyTest.kt b/core/tests/coretests/src/android/hardware/display/DisplayTopologyTest.kt new file mode 100644 index 000000000000..a6de611cc077 --- /dev/null +++ b/core/tests/coretests/src/android/hardware/display/DisplayTopologyTest.kt @@ -0,0 +1,472 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.display + +import android.hardware.display.DisplayTopology.TreeNode.POSITION_BOTTOM +import android.hardware.display.DisplayTopology.TreeNode.POSITION_TOP +import android.hardware.display.DisplayTopology.TreeNode.POSITION_RIGHT +import android.view.Display +import com.google.common.truth.Truth.assertThat +import org.junit.Test + +class DisplayTopologyTest { + private var topology = DisplayTopology() + + @Test + fun addOneDisplay() { + val displayId = 1 + val width = 800f + val height = 600f + + topology.addDisplay(displayId, width, height) + + assertThat(topology.primaryDisplayId).isEqualTo(displayId) + + val display = topology.root!! + assertThat(display.displayId).isEqualTo(displayId) + assertThat(display.width).isEqualTo(width) + assertThat(display.height).isEqualTo(height) + assertThat(display.children).isEmpty() + } + + @Test + fun addTwoDisplays() { + val displayId1 = 1 + val width1 = 800f + val height1 = 600f + + val displayId2 = 2 + val width2 = 1000f + val height2 = 1500f + + topology.addDisplay(displayId1, width1, height1) + topology.addDisplay(displayId2, width2, height2) + + assertThat(topology.primaryDisplayId).isEqualTo(displayId1) + + val display1 = topology.root!! + assertThat(display1.displayId).isEqualTo(displayId1) + assertThat(display1.width).isEqualTo(width1) + assertThat(display1.height).isEqualTo(height1) + assertThat(display1.children).hasSize(1) + + val display2 = display1.children[0] + assertThat(display2.displayId).isEqualTo(displayId2) + assertThat(display2.width).isEqualTo(width2) + assertThat(display2.height).isEqualTo(height2) + assertThat(display2.children).isEmpty() + assertThat(display2.position).isEqualTo(POSITION_TOP) + assertThat(display2.offset).isEqualTo(width1 / 2 - width2 / 2) + } + + @Test + fun addManyDisplays() { + val displayId1 = 1 + val width1 = 800f + val height1 = 600f + + val displayId2 = 2 + val width2 = 1000f + val height2 = 1500f + + topology.addDisplay(displayId1, width1, height1) + topology.addDisplay(displayId2, width2, height2) + + val noOfDisplays = 30 + for (i in 3..noOfDisplays) { + topology.addDisplay(/* displayId= */ i, width1, height1) + } + + assertThat(topology.primaryDisplayId).isEqualTo(displayId1) + + val display1 = topology.root!! + assertThat(display1.displayId).isEqualTo(displayId1) + assertThat(display1.width).isEqualTo(width1) + assertThat(display1.height).isEqualTo(height1) + assertThat(display1.children).hasSize(1) + + val display2 = display1.children[0] + assertThat(display2.displayId).isEqualTo(displayId2) + assertThat(display2.width).isEqualTo(width2) + assertThat(display2.height).isEqualTo(height2) + assertThat(display2.children).hasSize(1) + assertThat(display2.position).isEqualTo(POSITION_TOP) + assertThat(display2.offset).isEqualTo(width1 / 2 - width2 / 2) + + var display = display2 + for (i in 3..noOfDisplays) { + display = display.children[0] + assertThat(display.displayId).isEqualTo(i) + assertThat(display.width).isEqualTo(width1) + assertThat(display.height).isEqualTo(height1) + // The last display should have no children + assertThat(display.children).hasSize(if (i < noOfDisplays) 1 else 0) + assertThat(display.position).isEqualTo(POSITION_RIGHT) + assertThat(display.offset).isEqualTo(0) + } + } + + @Test + fun removeDisplays() { + val displayId1 = 1 + val width1 = 800f + val height1 = 600f + + val displayId2 = 2 + val width2 = 1000f + val height2 = 1500f + + topology.addDisplay(displayId1, width1, height1) + topology.addDisplay(displayId2, width2, height2) + + val noOfDisplays = 30 + for (i in 3..noOfDisplays) { + topology.addDisplay(/* displayId= */ i, width1, height1) + } + + var removedDisplays = arrayOf(20) + topology.removeDisplay(20) + + assertThat(topology.primaryDisplayId).isEqualTo(displayId1) + + var display1 = topology.root!! + assertThat(display1.displayId).isEqualTo(displayId1) + assertThat(display1.width).isEqualTo(width1) + assertThat(display1.height).isEqualTo(height1) + assertThat(display1.children).hasSize(1) + + var display2 = display1.children[0] + assertThat(display2.displayId).isEqualTo(displayId2) + assertThat(display2.width).isEqualTo(width2) + assertThat(display2.height).isEqualTo(height2) + assertThat(display2.children).hasSize(1) + assertThat(display2.position).isEqualTo(POSITION_TOP) + assertThat(display2.offset).isEqualTo(width1 / 2 - width2 / 2) + + var display = display2 + for (i in 3..noOfDisplays) { + if (i in removedDisplays) { + continue + } + display = display.children[0] + assertThat(display.displayId).isEqualTo(i) + assertThat(display.width).isEqualTo(width1) + assertThat(display.height).isEqualTo(height1) + // The last display should have no children + assertThat(display.children).hasSize(if (i < noOfDisplays) 1 else 0) + assertThat(display.position).isEqualTo(POSITION_RIGHT) + assertThat(display.offset).isEqualTo(0) + } + + topology.removeDisplay(22) + removedDisplays += 22 + topology.removeDisplay(23) + removedDisplays += 23 + topology.removeDisplay(25) + removedDisplays += 25 + + assertThat(topology.primaryDisplayId).isEqualTo(displayId1) + + display1 = topology.root!! + assertThat(display1.displayId).isEqualTo(displayId1) + assertThat(display1.width).isEqualTo(width1) + assertThat(display1.height).isEqualTo(height1) + assertThat(display1.children).hasSize(1) + + display2 = display1.children[0] + assertThat(display2.displayId).isEqualTo(displayId2) + assertThat(display2.width).isEqualTo(width2) + assertThat(display2.height).isEqualTo(height2) + assertThat(display2.children).hasSize(1) + assertThat(display2.position).isEqualTo(POSITION_TOP) + assertThat(display2.offset).isEqualTo(width1 / 2 - width2 / 2) + + display = display2 + for (i in 3..noOfDisplays) { + if (i in removedDisplays) { + continue + } + display = display.children[0] + assertThat(display.displayId).isEqualTo(i) + assertThat(display.width).isEqualTo(width1) + assertThat(display.height).isEqualTo(height1) + // The last display should have no children + assertThat(display.children).hasSize(if (i < noOfDisplays) 1 else 0) + assertThat(display.position).isEqualTo(POSITION_RIGHT) + assertThat(display.offset).isEqualTo(0) + } + } + + @Test + fun removeAllDisplays() { + val displayId = 1 + val width = 800f + val height = 600f + + topology.addDisplay(displayId, width, height) + topology.removeDisplay(displayId) + + assertThat(topology.primaryDisplayId).isEqualTo(Display.INVALID_DISPLAY) + assertThat(topology.root).isNull() + } + + @Test + fun removeDisplayThatDoesNotExist() { + val displayId = 1 + val width = 800f + val height = 600f + + topology.addDisplay(displayId, width, height) + topology.removeDisplay(3) + + assertThat(topology.primaryDisplayId).isEqualTo(displayId) + + val display = topology.root!! + assertThat(display.displayId).isEqualTo(displayId) + assertThat(display.width).isEqualTo(width) + assertThat(display.height).isEqualTo(height) + assertThat(display.children).isEmpty() + } + + @Test + fun removePrimaryDisplay() { + val displayId1 = 1 + val displayId2 = 2 + val width = 800f + val height = 600f + + topology = DisplayTopology(/* root= */ null, displayId2) + topology.addDisplay(displayId1, width, height) + topology.addDisplay(displayId2, width, height) + topology.removeDisplay(displayId2) + + assertThat(topology.primaryDisplayId).isEqualTo(displayId1) + val display = topology.root!! + assertThat(display.displayId).isEqualTo(displayId1) + assertThat(display.width).isEqualTo(width) + assertThat(display.height).isEqualTo(height) + assertThat(display.children).isEmpty() + } + + @Test + fun normalization_noOverlaps_leavesTopologyUnchanged() { + val display1 = DisplayTopology.TreeNode(/* displayId= */ 1, /* width= */ 200f, + /* height= */ 600f, /* position= */ 0, /* offset= */ 0f) + + val display2 = DisplayTopology.TreeNode(/* displayId= */ 2, /* width= */ 600f, + /* height= */ 200f, POSITION_RIGHT, /* offset= */ 0f) + display1.addChild(display2) + + val primaryDisplayId = 3 + val display3 = DisplayTopology.TreeNode(primaryDisplayId, /* width= */ 600f, + /* height= */ 200f, POSITION_RIGHT, /* offset= */ 400f) + display1.addChild(display3) + + val display4 = DisplayTopology.TreeNode(/* displayId= */ 4, /* width= */ 200f, + /* height= */ 600f, POSITION_RIGHT, /* offset= */ 0f) + display2.addChild(display4) + + topology = DisplayTopology(display1, primaryDisplayId) + topology.normalize() + + assertThat(topology.primaryDisplayId).isEqualTo(primaryDisplayId) + + val actualDisplay1 = topology.root!! + assertThat(actualDisplay1.displayId).isEqualTo(1) + assertThat(actualDisplay1.width).isEqualTo(200f) + assertThat(actualDisplay1.height).isEqualTo(600f) + assertThat(actualDisplay1.children).hasSize(2) + + val actualDisplay2 = actualDisplay1.children[0] + assertThat(actualDisplay2.displayId).isEqualTo(2) + assertThat(actualDisplay2.width).isEqualTo(600f) + assertThat(actualDisplay2.height).isEqualTo(200f) + assertThat(actualDisplay2.position).isEqualTo(POSITION_RIGHT) + assertThat(actualDisplay2.offset).isEqualTo(0f) + assertThat(actualDisplay2.children).hasSize(1) + + val actualDisplay3 = actualDisplay1.children[1] + assertThat(actualDisplay3.displayId).isEqualTo(3) + assertThat(actualDisplay3.width).isEqualTo(600f) + assertThat(actualDisplay3.height).isEqualTo(200f) + assertThat(actualDisplay3.position).isEqualTo(POSITION_RIGHT) + assertThat(actualDisplay3.offset).isEqualTo(400f) + assertThat(actualDisplay3.children).isEmpty() + + val actualDisplay4 = actualDisplay2.children[0] + assertThat(actualDisplay4.displayId).isEqualTo(4) + assertThat(actualDisplay4.width).isEqualTo(200f) + assertThat(actualDisplay4.height).isEqualTo(600f) + assertThat(actualDisplay4.position).isEqualTo(POSITION_RIGHT) + assertThat(actualDisplay4.offset).isEqualTo(0f) + assertThat(actualDisplay4.children).isEmpty() + } + + @Test + fun normalization_moveDisplayWithoutReparenting() { + val display1 = DisplayTopology.TreeNode(/* displayId= */ 1, /* width= */ 200f, + /* height= */ 600f, /* position= */ 0, /* offset= */ 0f) + + val display2 = DisplayTopology.TreeNode(/* displayId= */ 2, /* width= */ 200f, + /* height= */ 600f, POSITION_RIGHT, /* offset= */ 0f) + display1.addChild(display2) + + val primaryDisplayId = 3 + val display3 = DisplayTopology.TreeNode(primaryDisplayId, /* width= */ 600f, + /* height= */ 200f, POSITION_RIGHT, /* offset= */ 10f) + display1.addChild(display3) + + val display4 = DisplayTopology.TreeNode(/* displayId= */ 4, /* width= */ 200f, + /* height= */ 600f, POSITION_RIGHT, /* offset= */ 0f) + display2.addChild(display4) + + topology = DisplayTopology(display1, primaryDisplayId) + // Display 3 becomes a child of display 2. Display 4 gets moved without changing its parent. + topology.normalize() + + assertThat(topology.primaryDisplayId).isEqualTo(primaryDisplayId) + + val actualDisplay1 = topology.root!! + assertThat(actualDisplay1.displayId).isEqualTo(1) + assertThat(actualDisplay1.width).isEqualTo(200f) + assertThat(actualDisplay1.height).isEqualTo(600f) + assertThat(actualDisplay1.children).hasSize(1) + + val actualDisplay2 = actualDisplay1.children[0] + assertThat(actualDisplay2.displayId).isEqualTo(2) + assertThat(actualDisplay2.width).isEqualTo(200f) + assertThat(actualDisplay2.height).isEqualTo(600f) + assertThat(actualDisplay2.position).isEqualTo(POSITION_RIGHT) + assertThat(actualDisplay2.offset).isEqualTo(0f) + assertThat(actualDisplay2.children).hasSize(2) + + val actualDisplay3 = actualDisplay2.children[1] + assertThat(actualDisplay3.displayId).isEqualTo(3) + assertThat(actualDisplay3.width).isEqualTo(600f) + assertThat(actualDisplay3.height).isEqualTo(200f) + assertThat(actualDisplay3.position).isEqualTo(POSITION_RIGHT) + assertThat(actualDisplay3.offset).isEqualTo(10f) + assertThat(actualDisplay3.children).isEmpty() + + val actualDisplay4 = actualDisplay2.children[0] + assertThat(actualDisplay4.displayId).isEqualTo(4) + assertThat(actualDisplay4.width).isEqualTo(200f) + assertThat(actualDisplay4.height).isEqualTo(600f) + assertThat(actualDisplay4.position).isEqualTo(POSITION_RIGHT) + assertThat(actualDisplay4.offset).isEqualTo(210f) + assertThat(actualDisplay4.children).isEmpty() + } + + @Test + fun normalization_moveDisplayWithoutReparenting_offsetOutOfBounds() { + val display1 = DisplayTopology.TreeNode(/* displayId= */ 1, /* width= */ 200f, + /* height= */ 50f, /* position= */ 0, /* offset= */ 0f) + + val display2 = DisplayTopology.TreeNode(/* displayId= */ 2, /* width= */ 600f, + /* height= */ 200f, POSITION_RIGHT, /* offset= */ 0f) + display1.addChild(display2) + + val primaryDisplayId = 3 + val display3 = DisplayTopology.TreeNode(primaryDisplayId, /* width= */ 600f, + /* height= */ 200f, POSITION_RIGHT, /* offset= */ 10f) + display1.addChild(display3) + + topology = DisplayTopology(display1, primaryDisplayId) + // Display 3 gets moved and its left side is still on the same line as the right side + // of Display 1, but it no longer touches it (the offset is out of bounds), so Display 2 + // becomes its new parent. + topology.normalize() + + assertThat(topology.primaryDisplayId).isEqualTo(primaryDisplayId) + + val actualDisplay1 = topology.root!! + assertThat(actualDisplay1.displayId).isEqualTo(1) + assertThat(actualDisplay1.width).isEqualTo(200f) + assertThat(actualDisplay1.height).isEqualTo(50f) + assertThat(actualDisplay1.children).hasSize(1) + + val actualDisplay2 = actualDisplay1.children[0] + assertThat(actualDisplay2.displayId).isEqualTo(2) + assertThat(actualDisplay2.width).isEqualTo(600f) + assertThat(actualDisplay2.height).isEqualTo(200f) + assertThat(actualDisplay2.position).isEqualTo(POSITION_RIGHT) + assertThat(actualDisplay2.offset).isEqualTo(0f) + assertThat(actualDisplay2.children).hasSize(1) + + val actualDisplay3 = actualDisplay2.children[0] + assertThat(actualDisplay3.displayId).isEqualTo(3) + assertThat(actualDisplay3.width).isEqualTo(600f) + assertThat(actualDisplay3.height).isEqualTo(200f) + assertThat(actualDisplay3.position).isEqualTo(POSITION_BOTTOM) + assertThat(actualDisplay3.offset).isEqualTo(0f) + assertThat(actualDisplay3.children).isEmpty() + } + + @Test + fun normalization_moveAndReparentDisplay() { + val display1 = DisplayTopology.TreeNode(/* displayId= */ 1, /* width= */ 200f, + /* height= */ 600f, /* position= */ 0, /* offset= */ 0f) + + val display2 = DisplayTopology.TreeNode(/* displayId= */ 2, /* width= */ 200f, + /* height= */ 600f, POSITION_RIGHT, /* offset= */ 0f) + display1.addChild(display2) + + val primaryDisplayId = 3 + val display3 = DisplayTopology.TreeNode(primaryDisplayId, /* width= */ 600f, + /* height= */ 200f, POSITION_RIGHT, /* offset= */ 400f) + display1.addChild(display3) + + val display4 = DisplayTopology.TreeNode(/* displayId= */ 4, /* width= */ 200f, + /* height= */ 600f, POSITION_RIGHT, /* offset= */ 0f) + display2.addChild(display4) + + topology = DisplayTopology(display1, primaryDisplayId) + topology.normalize() + + assertThat(topology.primaryDisplayId).isEqualTo(primaryDisplayId) + + val actualDisplay1 = topology.root!! + assertThat(actualDisplay1.displayId).isEqualTo(1) + assertThat(actualDisplay1.width).isEqualTo(200f) + assertThat(actualDisplay1.height).isEqualTo(600f) + assertThat(actualDisplay1.children).hasSize(1) + + val actualDisplay2 = actualDisplay1.children[0] + assertThat(actualDisplay2.displayId).isEqualTo(2) + assertThat(actualDisplay2.width).isEqualTo(200f) + assertThat(actualDisplay2.height).isEqualTo(600f) + assertThat(actualDisplay2.position).isEqualTo(POSITION_RIGHT) + assertThat(actualDisplay2.offset).isEqualTo(0f) + assertThat(actualDisplay2.children).hasSize(1) + + val actualDisplay3 = actualDisplay2.children[0] + assertThat(actualDisplay3.displayId).isEqualTo(3) + assertThat(actualDisplay3.width).isEqualTo(600f) + assertThat(actualDisplay3.height).isEqualTo(200f) + assertThat(actualDisplay3.position).isEqualTo(POSITION_RIGHT) + assertThat(actualDisplay3.offset).isEqualTo(400f) + assertThat(actualDisplay3.children).hasSize(1) + + val actualDisplay4 = actualDisplay3.children[0] + assertThat(actualDisplay4.displayId).isEqualTo(4) + assertThat(actualDisplay4.width).isEqualTo(200f) + assertThat(actualDisplay4.height).isEqualTo(600f) + assertThat(actualDisplay4.position).isEqualTo(POSITION_RIGHT) + assertThat(actualDisplay4.offset).isEqualTo(-400f) + assertThat(actualDisplay4.children).isEmpty() + } +}
\ No newline at end of file diff --git a/core/tests/coretests/src/android/view/DisplayInfoTest.java b/core/tests/coretests/src/android/view/DisplayInfoTest.java index 4c5b7e508e34..8932cf1ba552 100644 --- a/core/tests/coretests/src/android/view/DisplayInfoTest.java +++ b/core/tests/coretests/src/android/view/DisplayInfoTest.java @@ -78,6 +78,23 @@ public class DisplayInfoTest { } @Test + public void testRefreshRateOverride_keepsDisplyInfosEqualWhenOverrideIsSame() { + Display.Mode mode = new Display.Mode( + /*modeId=*/1, /*width=*/1000, /*height=*/1000, /*refreshRate=*/120); + DisplayInfo displayInfo1 = new DisplayInfo(); + setSupportedMode(displayInfo1, mode); + displayInfo1.renderFrameRate = 60; + displayInfo1.refreshRateOverride = 30; + + DisplayInfo displayInfo2 = new DisplayInfo(); + setSupportedMode(displayInfo2, mode); + displayInfo2.renderFrameRate = 30; + displayInfo2.refreshRateOverride = 30; + + assertTrue(displayInfo1.equals(displayInfo2)); + } + + @Test public void testRefreshRateOverride_makeDisplayInfosDifferent() { Display.Mode mode = new Display.Mode( /*modeId=*/1, /*width=*/1000, /*height=*/1000, /*refreshRate=*/120); diff --git a/core/tests/coretests/src/android/view/HapticScrollFeedbackProviderTest.java b/core/tests/coretests/src/android/view/HapticScrollFeedbackProviderTest.java index ac6c19e79fcb..66cf9c7ec832 100644 --- a/core/tests/coretests/src/android/view/HapticScrollFeedbackProviderTest.java +++ b/core/tests/coretests/src/android/view/HapticScrollFeedbackProviderTest.java @@ -17,6 +17,7 @@ package android.view; import static android.os.vibrator.Flags.FLAG_HAPTIC_FEEDBACK_INPUT_SOURCE_CUSTOMIZATION_ENABLED; +import static android.view.flags.Flags.FLAG_DYNAMIC_VIEW_ROTARY_HAPTICS_CONFIGURATION; import static android.view.HapticFeedbackConstants.SCROLL_ITEM_FOCUS; import static android.view.HapticFeedbackConstants.SCROLL_LIMIT; import static android.view.HapticFeedbackConstants.SCROLL_TICK; @@ -74,12 +75,13 @@ public final class HapticScrollFeedbackProviderTest { mView = new TestView(InstrumentationRegistry.getContext()); mProvider = new HapticScrollFeedbackProvider(mView, mMockViewConfig, - /* disabledIfViewPlaysScrollHaptics= */ true); + /* isFromView= */ false); mSetFlagsRule.disableFlags(FLAG_HAPTIC_FEEDBACK_INPUT_SOURCE_CUSTOMIZATION_ENABLED); } @Test public void testRotaryEncoder_noFeedbackWhenViewBasedFeedbackIsEnabled() { + mSetFlagsRule.disableFlags(FLAG_DYNAMIC_VIEW_ROTARY_HAPTICS_CONFIGURATION); when(mMockViewConfig.isViewBasedRotaryEncoderHapticScrollFeedbackEnabled()) .thenReturn(true); setHapticScrollTickInterval(5); @@ -97,7 +99,24 @@ public final class HapticScrollFeedbackProviderTest { } @Test + public void testRotaryEncoder_dynamicViewRotaryFeedback_enabledEvenWhenViewFeedbackIsEnabled() { + mSetFlagsRule.enableFlags(FLAG_DYNAMIC_VIEW_ROTARY_HAPTICS_CONFIGURATION); + when(mMockViewConfig.isViewBasedRotaryEncoderHapticScrollFeedbackEnabled()) + .thenReturn(true); + setHapticScrollTickInterval(5); + mProvider = new HapticScrollFeedbackProvider(mView, mMockViewConfig, + /* isFromView= */ false); + + mProvider.onScrollProgress( + INPUT_DEVICE_1, InputDevice.SOURCE_ROTARY_ENCODER, MotionEvent.AXIS_SCROLL, + /* deltaInPixels= */ 10); + + assertFeedbackCount(mView, SCROLL_TICK, 1); + } + + @Test public void testRotaryEncoder_inputDeviceCustomized_noFeedbackWhenViewBasedFeedbackIsEnabled() { + mSetFlagsRule.disableFlags(FLAG_DYNAMIC_VIEW_ROTARY_HAPTICS_CONFIGURATION); mSetFlagsRule.enableFlags(FLAG_HAPTIC_FEEDBACK_INPUT_SOURCE_CUSTOMIZATION_ENABLED); when(mMockViewConfig.isViewBasedRotaryEncoderHapticScrollFeedbackEnabled()) @@ -119,7 +138,7 @@ public final class HapticScrollFeedbackProviderTest { @Test public void testRotaryEncoder_feedbackWhenDisregardingViewBasedScrollHaptics() { mProvider = new HapticScrollFeedbackProvider(mView, mMockViewConfig, - /* disabledIfViewPlaysScrollHaptics= */ false); + /* isFromView= */ true); when(mMockViewConfig.isViewBasedRotaryEncoderHapticScrollFeedbackEnabled()) .thenReturn(true); setHapticScrollTickInterval(5); @@ -144,7 +163,7 @@ public final class HapticScrollFeedbackProviderTest { List<HapticFeedbackRequest> requests = new ArrayList<>(); mProvider = new HapticScrollFeedbackProvider(mView, mMockViewConfig, - /* disabledIfViewPlaysScrollHaptics= */ false); + /* isFromView= */ true); when(mMockViewConfig.isViewBasedRotaryEncoderHapticScrollFeedbackEnabled()) .thenReturn(true); setHapticScrollTickInterval(5); @@ -917,19 +936,20 @@ public final class HapticScrollFeedbackProviderTest { @Test public void testNonRotaryInputFeedbackNotBlockedByRotaryUnavailability() { + mSetFlagsRule.disableFlags(FLAG_DYNAMIC_VIEW_ROTARY_HAPTICS_CONFIGURATION); when(mMockViewConfig.isViewBasedRotaryEncoderHapticScrollFeedbackEnabled()) .thenReturn(true); setHapticScrollFeedbackEnabled(true); setHapticScrollTickInterval(5); mProvider = new HapticScrollFeedbackProvider(mView, mMockViewConfig, - /* disabledIfViewPlaysScrollHaptics= */ true); + /* isFromView= */ false); // Expect one feedback here. Touch input should provide feedback since scroll feedback has // been enabled via `setHapticScrollFeedbackEnabled(true)`. mProvider.onScrollProgress( INPUT_DEVICE_1, InputDevice.SOURCE_TOUCHSCREEN, MotionEvent.AXIS_Y, /* deltaInPixels= */ 10); - // Because `isViewBasedRotaryEncoderHapticScrollFeedbackEnabled()` is false and + // Because `isViewBasedRotaryEncoderHapticScrollFeedbackEnabled()` is true and // `disabledIfViewPlaysScrollHaptics` is true, the scroll progress from rotary encoders will // produce no feedback. mProvider.onScrollProgress( diff --git a/core/tests/coretests/src/android/view/RotaryScrollHapticsTest.java b/core/tests/coretests/src/android/view/RotaryScrollHapticsTest.java index 9a5c1c5112e6..b1a56373c9d6 100644 --- a/core/tests/coretests/src/android/view/RotaryScrollHapticsTest.java +++ b/core/tests/coretests/src/android/view/RotaryScrollHapticsTest.java @@ -30,8 +30,12 @@ import static org.mockito.Mockito.when; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; +import android.annotation.Nullable; import android.content.Context; +import android.platform.test.annotations.EnableFlags; import android.platform.test.annotations.Presubmit; +import android.platform.test.flag.junit.SetFlagsRule; +import android.view.flags.Flags; import androidx.test.InstrumentationRegistry; import androidx.test.ext.junit.runners.AndroidJUnit4; @@ -39,6 +43,7 @@ import androidx.test.filters.SmallTest; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; @@ -48,6 +53,8 @@ import org.mockito.Mock; @RunWith(AndroidJUnit4.class) @Presubmit public final class RotaryScrollHapticsTest { + @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + private static final int TEST_ROTARY_DEVICE_ID = 1; private static final int TEST_RANDOM_DEVICE_ID = 2; @@ -167,6 +174,26 @@ public final class RotaryScrollHapticsTest { } @Test + @EnableFlags(Flags.FLAG_DYNAMIC_VIEW_ROTARY_HAPTICS_CONFIGURATION) + public void testChildViewImplementationUsesScrollFeedbackProvider_doesNoScrollFeedback() { + mView.configureGenericMotion(/* result= */ false, /* scroll= */ true); + mView.mUsesCustomScrollFeedbackProvider = true; + + // Send multiple generic motion events, to catch bugs where the behavior is WAI only for the + // first dispatch, but buggy for future calls. + mView.dispatchGenericMotionEvent(createRotaryEvent(20)); + mView.dispatchGenericMotionEvent(createRotaryEvent(10)); + mView.dispatchGenericMotionEvent(createRotaryEvent(30)); + + // Verify that the base View class's ScrollFeedbackProvider produces no scroll progress + // or limit events, because there's a custom ScrollFeedbackProvider used by the child + // View class implementation, which should hint the base View class to disable its own + // ScrollFeedbackProvider usage. + verifyNoScrollProgress(); + verifyNoScrollLimit(); + } + + @Test public void testScrollProgress_genericMotionEventCallbackReturningTrue_doesScrollProgress() { mView.configureGenericMotion(/* result= */ true, /* scroll= */ true); @@ -208,6 +235,9 @@ public final class RotaryScrollHapticsTest { private static final class TestGenericMotionEventControllingView extends View { private boolean mGenericMotionResult; private boolean mScrollOnGenericMotion; + private boolean mUsesCustomScrollFeedbackProvider = false; + + @Nullable private ScrollFeedbackProvider mCustomScrollFeedbackProvider; TestGenericMotionEventControllingView(Context context) { super(context); @@ -222,6 +252,19 @@ public final class RotaryScrollHapticsTest { public boolean onGenericMotionEvent(MotionEvent event) { if (mScrollOnGenericMotion) { scrollTo(100, 200); // scroll values random (not relevant for tests). + if (mUsesCustomScrollFeedbackProvider) { + // Mimic how a real child class of View would instantiate and use the + // ScrollFeedbackProvider API. + if (mCustomScrollFeedbackProvider == null) { + mCustomScrollFeedbackProvider = ScrollFeedbackProvider.createProvider(this); + } + float axisScrollValue = event.getAxisValue(AXIS_SCROLL); + mCustomScrollFeedbackProvider.onScrollProgress( + event.getDeviceId(), + event.getSource(), + MotionEvent.AXIS_SCROLL, + (int) (axisScrollValue * TEST_SCALED_VERTICAL_SCROLL_FACTOR)); + } } return mGenericMotionResult; } diff --git a/core/tests/coretests/src/com/android/internal/accessibility/util/ShortcutUtilsTest.java b/core/tests/coretests/src/com/android/internal/accessibility/util/ShortcutUtilsTest.java index 8bebc62e93f2..1a9af6b55eed 100644 --- a/core/tests/coretests/src/com/android/internal/accessibility/util/ShortcutUtilsTest.java +++ b/core/tests/coretests/src/com/android/internal/accessibility/util/ShortcutUtilsTest.java @@ -21,6 +21,7 @@ import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_MAG import static com.android.internal.accessibility.common.ShortcutConstants.SERVICES_SEPARATOR; import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.GESTURE; +import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.KEY_GESTURE; import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE; import static com.google.common.truth.Truth.assertThat; @@ -123,6 +124,14 @@ public class ShortcutUtilsTest { } @Test + public void getShortcutTargets_keyGestureShortcutNoService_emptyResult() { + assertThat( + ShortcutUtils.getShortcutTargetsFromSettings( + mContext, KEY_GESTURE, mDefaultUserId) + ).isEmpty(); + } + + @Test public void getShortcutTargets_softwareShortcut1Service_return1Service() { setupShortcutTargets(ONE_COMPONENT, Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS); setupShortcutTargets(TWO_COMPONENTS, Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE); diff --git a/data/etc/platform.xml b/data/etc/platform.xml index 7b96699f7f71..baaec86828eb 100644 --- a/data/etc/platform.xml +++ b/data/etc/platform.xml @@ -213,6 +213,8 @@ <assign-permission name="android.permission.REGISTER_STATS_PULL_ATOM" uid="gpu_service" /> <assign-permission name="android.permission.REGISTER_STATS_PULL_ATOM" uid="keystore" /> + <assign-permission name="android.permission.DYNAMIC_INSTRUMENTATION" uid="uprobestats" /> + <split-permission name="android.permission.ACCESS_FINE_LOCATION"> <new-permission name="android.permission.ACCESS_COARSE_LOCATION" /> </split-permission> @@ -271,6 +273,16 @@ targetSdk="33"> <new-permission name="android.permission.BODY_SENSORS_BACKGROUND" /> </split-permission> + <split-permission name="android.permission.BODY_SENSORS" + featureFlag="android.permission.flags.replace_body_sensor_permission_enabled" + targetSdk="36"> + <new-permission name="android.permission.health.READ_HEART_RATE" /> + </split-permission> + <split-permission name="android.permission.BODY_SENSORS_BACKGROUND" + featureFlag="android.permission.flags.replace_body_sensor_permission_enabled" + targetSdk="36"> + <new-permission name="android.permission.health.READ_HEALTH_DATA_IN_BACKGROUND" /> + </split-permission> <split-permission name="android.permission.READ_EXTERNAL_STORAGE" targetSdk="33"> <new-permission name="android.permission.READ_MEDIA_AUDIO" /> diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index 7ced809d2a3a..541ca602a386 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -594,6 +594,9 @@ applications that come with the platform <!-- Permission required for CTS test - AdvancedProtectionManagerTest --> <permission name="android.permission.SET_ADVANCED_PROTECTION_MODE" /> <permission name="android.permission.QUERY_ADVANCED_PROTECTION_MODE" /> + <!-- Permissions required for CTS test - SettingsPreferenceServiceClientTest --> + <permission name="android.permission.READ_SYSTEM_PREFERENCES" /> + <permission name="android.permission.WRITE_SYSTEM_PREFERENCES" /> </privapp-permissions> <privapp-permissions package="com.android.statementservice"> diff --git a/data/keyboards/Generic.kl b/data/keyboards/Generic.kl index f8d3bffbe00b..2b0802b54c14 100644 --- a/data/keyboards/Generic.kl +++ b/data/keyboards/Generic.kl @@ -171,7 +171,7 @@ key 143 WAKEUP # key 149 "KEY_PROG2" key 150 EXPLORER # key 151 "KEY_MSDOS" -key 152 POWER +key 152 LOCK # key 153 "KEY_DIRECTION" # key 154 "KEY_CYCLEWINDOWS" key 155 ENVELOPE @@ -200,20 +200,20 @@ key 177 PAGE_UP key 178 PAGE_DOWN key 179 NUMPAD_LEFT_PAREN key 180 NUMPAD_RIGHT_PAREN -# key 181 "KEY_NEW" +key 181 NEW # key 182 "KEY_REDO" -# key 183 F13 -# key 184 F14 -# key 185 F15 -# key 186 F16 -# key 187 F17 -# key 188 F18 -# key 189 F19 -# key 190 F20 -# key 191 F21 -# key 192 F22 -# key 193 F23 -# key 194 F24 +key 183 F13 +key 184 F14 +key 185 F15 +key 186 F16 +key 187 F17 +key 188 F18 +key 189 F19 +key 190 F20 +key 191 F21 +key 192 F22 +key 193 F23 +key 194 F24 # key 195 (undefined) # key 196 (undefined) # key 197 (undefined) @@ -225,11 +225,11 @@ key 201 MEDIA_PAUSE # key 203 "KEY_PROG4" key 204 NOTIFICATION # key 205 "KEY_SUSPEND" -# key 206 "KEY_CLOSE" +key 206 CLOSE key 207 MEDIA_PLAY key 208 MEDIA_FAST_FORWARD # key 209 "KEY_BASSBOOST" -# key 210 "KEY_PRINT" +key 210 PRINT # key 211 "KEY_HP" key 212 CAMERA key 213 MUSIC @@ -328,7 +328,7 @@ key 368 LANGUAGE_SWITCH # key 369 "KEY_TITLE" key 370 CAPTIONS # key 371 "KEY_ANGLE" -# key 372 "KEY_ZOOM" +key 372 FULLSCREEN # key 373 "KEY_MODE" # key 374 "KEY_KEYBOARD" # key 375 "KEY_SCREEN" @@ -425,12 +425,15 @@ key 582 VOICE_ASSIST # Linux KEY_ASSISTANT key 583 ASSIST key 585 EMOJI_PICKER +key 586 DICTATE key 656 MACRO_1 key 657 MACRO_2 key 658 MACRO_3 key 659 MACRO_4 # Keys defined by HID usages +key usage 0x010082 LOCK FALLBACK_USAGE_MAPPING +key usage 0x01009B DO_NOT_DISTURB FALLBACK_USAGE_MAPPING key usage 0x0c0065 SCREENSHOT FALLBACK_USAGE_MAPPING key usage 0x0c0067 WINDOW FALLBACK_USAGE_MAPPING key usage 0x0c006F BRIGHTNESS_UP FALLBACK_USAGE_MAPPING @@ -438,12 +441,17 @@ key usage 0x0c0070 BRIGHTNESS_DOWN FALLBACK_USAGE_MAPPING key usage 0x0c0079 KEYBOARD_BACKLIGHT_UP FALLBACK_USAGE_MAPPING key usage 0x0c007A KEYBOARD_BACKLIGHT_DOWN FALLBACK_USAGE_MAPPING key usage 0x0c007C KEYBOARD_BACKLIGHT_TOGGLE FALLBACK_USAGE_MAPPING +key usage 0x0c00D8 DICTATE FALLBACK_USAGE_MAPPING key usage 0x0c00D9 EMOJI_PICKER FALLBACK_USAGE_MAPPING key usage 0x0c0173 MEDIA_AUDIO_TRACK FALLBACK_USAGE_MAPPING key usage 0x0c019C PROFILE_SWITCH FALLBACK_USAGE_MAPPING key usage 0x0c019F SETTINGS FALLBACK_USAGE_MAPPING key usage 0x0c01A2 ALL_APPS FALLBACK_USAGE_MAPPING +key usage 0x0c0201 NEW FALLBACK_USAGE_MAPPING +key usage 0x0c0203 CLOSE FALLBACK_USAGE_MAPPING +key usage 0x0c0208 PRINT FALLBACK_USAGE_MAPPING key usage 0x0c0227 REFRESH FALLBACK_USAGE_MAPPING +key usage 0x0c0232 FULLSCREEN FALLBACK_USAGE_MAPPING key usage 0x0c029D LANGUAGE_SWITCH FALLBACK_USAGE_MAPPING key usage 0x0c029F RECENT_APPS FALLBACK_USAGE_MAPPING key usage 0x0c02A2 ALL_APPS FALLBACK_USAGE_MAPPING diff --git a/data/sounds/Android.bp b/data/sounds/Android.bp new file mode 100644 index 000000000000..65d4872cdc16 --- /dev/null +++ b/data/sounds/Android.bp @@ -0,0 +1,304 @@ +// Copyright (C) 2024 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +phony { + name: "frameworks_sounds", + required: [ + "frameworks_alarm_sounds", + "frameworks_notifications_sounds", + "frameworks_ringtones_sounds", + "frameworks_ui_sounds", + "frameworks_ui_48k_sounds", + ], +} + +prebuilt_media { + name: "frameworks_alarm_sounds", + srcs: [ + "Alarm_Beep_01.ogg", + "Alarm_Beep_02.ogg", + "Alarm_Beep_03.ogg", + "Alarm_Buzzer.ogg", + "Alarm_Classic.ogg", + "Alarm_Rooster_02.ogg", + "alarms/ogg/Argon.ogg", + "alarms/ogg/Barium.ogg", + "alarms/ogg/Carbon.ogg", + "alarms/ogg/Helium.ogg", + "alarms/ogg/Krypton.ogg", + "alarms/ogg/Neon.ogg", + "alarms/ogg/Neptunium.ogg", + "alarms/ogg/Osmium.ogg", + "alarms/ogg/Oxygen.ogg", + "alarms/ogg/Platinum.ogg", + "alarms/ogg/Promethium.ogg", + "alarms/ogg/Scandium.ogg", + ], + relative_install_path: "audio/alarms", + product_specific: true, + no_full_install: true, +} + +prebuilt_media { + name: "frameworks_notifications_sounds", + srcs: [ + "notifications/ogg/Adara.ogg", + "notifications/Aldebaran.ogg", + "notifications/Altair.ogg", + "notifications/ogg/Alya.ogg", + "notifications/Antares.ogg", + "notifications/ogg/Antimony.ogg", + "notifications/ogg/Arcturus.ogg", + "notifications/ogg/Argon.ogg", + "notifications/Beat_Box_Android.ogg", + "notifications/ogg/Bellatrix.ogg", + "notifications/ogg/Beryllium.ogg", + "notifications/Betelgeuse.ogg", + "newwavelabs/CaffeineSnake.ogg", + "notifications/Canopus.ogg", + "notifications/ogg/Capella.ogg", + "notifications/Castor.ogg", + "notifications/ogg/CetiAlpha.ogg", + "notifications/ogg/Cobalt.ogg", + "notifications/Cricket.ogg", + "newwavelabs/DearDeer.ogg", + "notifications/Deneb.ogg", + "notifications/Doink.ogg", + "newwavelabs/DontPanic.ogg", + "notifications/Drip.ogg", + "notifications/Electra.ogg", + "F1_MissedCall.ogg", + "F1_New_MMS.ogg", + "F1_New_SMS.ogg", + "notifications/ogg/Fluorine.ogg", + "notifications/Fomalhaut.ogg", + "notifications/ogg/Gallium.ogg", + "notifications/Heaven.ogg", + "notifications/ogg/Helium.ogg", + "newwavelabs/Highwire.ogg", + "notifications/ogg/Hojus.ogg", + "notifications/ogg/Iridium.ogg", + "notifications/ogg/Krypton.ogg", + "newwavelabs/KzurbSonar.ogg", + "notifications/ogg/Lalande.ogg", + "notifications/Merope.ogg", + "notifications/ogg/Mira.ogg", + "newwavelabs/OnTheHunt.ogg", + "notifications/ogg/Palladium.ogg", + "notifications/Plastic_Pipe.ogg", + "notifications/ogg/Polaris.ogg", + "notifications/ogg/Pollux.ogg", + "notifications/ogg/Procyon.ogg", + "notifications/ogg/Proxima.ogg", + "notifications/ogg/Radon.ogg", + "notifications/ogg/Rubidium.ogg", + "notifications/ogg/Selenium.ogg", + "notifications/ogg/Shaula.ogg", + "notifications/Sirrah.ogg", + "notifications/SpaceSeed.ogg", + "notifications/ogg/Spica.ogg", + "notifications/ogg/Strontium.ogg", + "notifications/ogg/Syrma.ogg", + "notifications/TaDa.ogg", + "notifications/ogg/Talitha.ogg", + "notifications/ogg/Tejat.ogg", + "notifications/ogg/Thallium.ogg", + "notifications/Tinkerbell.ogg", + "notifications/ogg/Upsilon.ogg", + "notifications/ogg/Vega.ogg", + "newwavelabs/Voila.ogg", + "notifications/ogg/Xenon.ogg", + "notifications/ogg/Zirconium.ogg", + "notifications/arcturus.ogg", + "notifications/moonbeam.ogg", + "notifications/pixiedust.ogg", + "notifications/pizzicato.ogg", + "notifications/regulus.ogg", + "notifications/sirius.ogg", + "notifications/tweeters.ogg", + "notifications/vega.ogg", + ], + relative_install_path: "audio/notifications", + product_specific: true, + no_full_install: true, +} + +prebuilt_media { + name: "frameworks_ringtones_sounds", + srcs: [ + "ringtones/ANDROMEDA.ogg", + "ringtones/ogg/Andromeda.ogg", + "ringtones/ogg/Aquila.ogg", + "ringtones/ogg/ArgoNavis.ogg", + "ringtones/ogg/Atria.ogg", + "ringtones/BOOTES.ogg", + "newwavelabs/Backroad.ogg", + "newwavelabs/BeatPlucker.ogg", + "newwavelabs/BentleyDubs.ogg", + "newwavelabs/Big_Easy.ogg", + "newwavelabs/BirdLoop.ogg", + "newwavelabs/Bollywood.ogg", + "newwavelabs/BussaMove.ogg", + "ringtones/CANISMAJOR.ogg", + "ringtones/CASSIOPEIA.ogg", + "newwavelabs/Cairo.ogg", + "newwavelabs/Calypso_Steel.ogg", + "ringtones/ogg/CanisMajor.ogg", + "newwavelabs/CaribbeanIce.ogg", + "ringtones/ogg/Carina.ogg", + "ringtones/ogg/Centaurus.ogg", + "newwavelabs/Champagne_Edition.ogg", + "newwavelabs/Club_Cubano.ogg", + "newwavelabs/CrayonRock.ogg", + "newwavelabs/CrazyDream.ogg", + "newwavelabs/CurveBall.ogg", + "ringtones/ogg/Cygnus.ogg", + "newwavelabs/DancinFool.ogg", + "newwavelabs/Ding.ogg", + "newwavelabs/DonMessWivIt.ogg", + "ringtones/ogg/Draco.ogg", + "newwavelabs/DreamTheme.ogg", + "newwavelabs/Eastern_Sky.ogg", + "newwavelabs/Enter_the_Nexus.ogg", + "ringtones/Eridani.ogg", + "newwavelabs/EtherShake.ogg", + "ringtones/FreeFlight.ogg", + "newwavelabs/FriendlyGhost.ogg", + "newwavelabs/Funk_Yall.ogg", + "newwavelabs/GameOverGuitar.ogg", + "newwavelabs/Gimme_Mo_Town.ogg", + "ringtones/ogg/Girtab.ogg", + "newwavelabs/Glacial_Groove.ogg", + "newwavelabs/Growl.ogg", + "newwavelabs/HalfwayHome.ogg", + "ringtones/ogg/Hydra.ogg", + "newwavelabs/InsertCoin.ogg", + "ringtones/ogg/Kuma.ogg", + "newwavelabs/LoopyLounge.ogg", + "newwavelabs/LoveFlute.ogg", + "ringtones/Lyra.ogg", + "ringtones/ogg/Machina.ogg", + "newwavelabs/MidEvilJaunt.ogg", + "newwavelabs/MildlyAlarming.ogg", + "newwavelabs/Nairobi.ogg", + "newwavelabs/Nassau.ogg", + "newwavelabs/NewPlayer.ogg", + "newwavelabs/No_Limits.ogg", + "newwavelabs/Noises1.ogg", + "newwavelabs/Noises2.ogg", + "newwavelabs/Noises3.ogg", + "newwavelabs/OrganDub.ogg", + "ringtones/ogg/Orion.ogg", + "ringtones/PERSEUS.ogg", + "newwavelabs/Paradise_Island.ogg", + "ringtones/ogg/Pegasus.ogg", + "ringtones/ogg/Perseus.ogg", + "newwavelabs/Playa.ogg", + "ringtones/ogg/Pyxis.ogg", + "ringtones/ogg/Rasalas.ogg", + "newwavelabs/Revelation.ogg", + "ringtones/ogg/Rigel.ogg", + "Ring_Classic_02.ogg", + "Ring_Digital_02.ogg", + "Ring_Synth_02.ogg", + "Ring_Synth_04.ogg", + "newwavelabs/Road_Trip.ogg", + "newwavelabs/RomancingTheTone.ogg", + "newwavelabs/Safari.ogg", + "newwavelabs/Savannah.ogg", + "ringtones/ogg/Scarabaeus.ogg", + "ringtones/ogg/Sceptrum.ogg", + "newwavelabs/Seville.ogg", + "newwavelabs/Shes_All_That.ogg", + "newwavelabs/SilkyWay.ogg", + "newwavelabs/SitarVsSitar.ogg", + "ringtones/ogg/Solarium.ogg", + "newwavelabs/SpringyJalopy.ogg", + "newwavelabs/Steppin_Out.ogg", + "newwavelabs/Terminated.ogg", + "ringtones/Testudo.ogg", + "ringtones/ogg/Themos.ogg", + "newwavelabs/Third_Eye.ogg", + "newwavelabs/Thunderfoot.ogg", + "newwavelabs/TwirlAway.ogg", + "ringtones/URSAMINOR.ogg", + "ringtones/ogg/UrsaMinor.ogg", + "newwavelabs/VeryAlarmed.ogg", + "ringtones/Vespa.ogg", + "newwavelabs/World.ogg", + "ringtones/ogg/Zeta.ogg", + "ringtones/hydra.ogg", + ], + relative_install_path: "audio/ringtones", + product_specific: true, + no_full_install: true, +} + +prebuilt_media { + name: "frameworks_ui_48k_sounds", + srcs: [ + "effects/ogg/Effect_Tick_48k.ogg", + "effects/ogg/KeypressDelete_120_48k.ogg", + "effects/ogg/KeypressReturn_120_48k.ogg", + "effects/ogg/KeypressSpacebar_120_48k.ogg", + "effects/ogg/KeypressStandard_120_48k.ogg", + "effects/ogg/KeypressInvalid_120_48k.ogg", + "effects/ogg/Trusted_48k.ogg", + "effects/ogg/VideoRecord_48k.ogg", + "effects/ogg/VideoStop_48k.ogg", + "effects/ogg/camera_click_48k.ogg", + ], + dsts: [ + "Effect_Tick.ogg", + "KeypressDelete.ogg", + "KeypressReturn.ogg", + "KeypressSpacebar.ogg", + "KeypressStandard.ogg", + "KeypressInvalid.ogg", + "Trusted.ogg", + "VideoRecord.ogg", + "VideoStop.ogg", + "camera_click.ogg", + ], + relative_install_path: "audio/ui", + product_specific: true, + no_full_install: true, +} + +prebuilt_media { + name: "frameworks_ui_sounds", + srcs: [ + "effects/ogg/Dock.ogg", + "effects/ogg/Lock.ogg", + "effects/ogg/LowBattery.ogg", + "effects/ogg/Undock.ogg", + "effects/ogg/Unlock.ogg", + "effects/ogg/WirelessChargingStarted.ogg", + "effects/ogg/camera_focus.ogg", + "effects/ogg/ChargingStarted.ogg", + "effects/ogg/InCallNotification.ogg", + "effects/ogg/NFCFailure.ogg", + "effects/ogg/NFCInitiated.ogg", + "effects/ogg/NFCSuccess.ogg", + "effects/ogg/NFCTransferComplete.ogg", + "effects/ogg/NFCTransferInitiated.ogg", + ], + relative_install_path: "audio/ui", + product_specific: true, + no_full_install: true, +} diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientParcelableChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientParcelableChecker.java index cae5d8e6846d..35b2375fbc46 100644 --- a/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientParcelableChecker.java +++ b/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientParcelableChecker.java @@ -96,7 +96,7 @@ public final class EfficientParcelableChecker extends BugChecker } if (WRITE_PARCELABLE.matches(tree, state)) { return buildDescription(tree) - .setMessage("Recommended to use 'item.writeToParcel()' to improve " + .setMessage("Recommended to use 'writeTypedObject()' to improve " + "efficiency; saves overhead of Parcelable class name") .build(); } diff --git a/graphics/java/android/graphics/ColorSpace.java b/graphics/java/android/graphics/ColorSpace.java index 4c47de0ca754..d55a71e21931 100644 --- a/graphics/java/android/graphics/ColorSpace.java +++ b/graphics/java/android/graphics/ColorSpace.java @@ -1761,7 +1761,7 @@ public abstract class ColorSpace { if (Flags.displayBt2020Colorspace()) { sNamedColorSpaceMap.put(Named.DISPLAY_BT2020.ordinal(), new ColorSpace.Rgb( - "BT 2020", + "Display BT. 2020", BT2020_PRIMARIES, ILLUMINANT_D65, null, diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java index 8bb32568ec5a..56bb0f0d12d5 100644 --- a/graphics/java/android/graphics/Paint.java +++ b/graphics/java/android/graphics/Paint.java @@ -2119,7 +2119,7 @@ public class Paint { * @see FontVariationAxis */ public boolean setFontVariationSettings(String fontVariationSettings) { - final boolean useFontVariationStore = Flags.typefaceRedesign() + final boolean useFontVariationStore = Flags.typefaceRedesignReadonly() && CompatChanges.isChangeEnabled(NEW_FONT_VARIATION_MANAGEMENT); if (useFontVariationStore) { FontVariationAxis[] axes = diff --git a/graphics/java/android/graphics/text/PositionedGlyphs.java b/graphics/java/android/graphics/text/PositionedGlyphs.java index ed17fdefcb53..43216ba6e087 100644 --- a/graphics/java/android/graphics/text/PositionedGlyphs.java +++ b/graphics/java/android/graphics/text/PositionedGlyphs.java @@ -133,7 +133,7 @@ public final class PositionedGlyphs { @NonNull public Font getFont(@IntRange(from = 0) int index) { Preconditions.checkArgumentInRange(index, 0, glyphCount() - 1, "index"); - if (Flags.typefaceRedesign()) { + if (Flags.typefaceRedesignReadonly()) { return mFonts.get(nGetFontId(mLayoutPtr, index)); } return mFonts.get(index); @@ -252,7 +252,7 @@ public final class PositionedGlyphs { mXOffset = xOffset; mYOffset = yOffset; - if (Flags.typefaceRedesign()) { + if (Flags.typefaceRedesignReadonly()) { int fontCount = nGetFontCount(layoutPtr); mFonts = new ArrayList<>(fontCount); for (int i = 0; i < fontCount; ++i) { diff --git a/keystore/java/android/security/keystore/KeyStoreManager.java b/keystore/java/android/security/keystore/KeyStoreManager.java index 197aaba4bcb5..e6091c1da8a5 100644 --- a/keystore/java/android/security/keystore/KeyStoreManager.java +++ b/keystore/java/android/security/keystore/KeyStoreManager.java @@ -49,7 +49,7 @@ import java.util.List; */ @FlaggedApi(android.security.Flags.FLAG_KEYSTORE_GRANT_API) @SystemService(Context.KEYSTORE_SERVICE) -public class KeyStoreManager { +public final class KeyStoreManager { private static final String TAG = "KeyStoreManager"; private static final Object sInstanceLock = new Object(); diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java index 4d7be39ca5a4..76eb207a31c9 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java @@ -19,6 +19,7 @@ package androidx.window.extensions.area; import static android.hardware.devicestate.DeviceState.PROPERTY_EMULATED_ONLY; import static android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT; import static android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_REAR_DISPLAY; +import static android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_REAR_DISPLAY_OUTER_DEFAULT; import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY; import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE; import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE_IDENTIFIER; @@ -104,6 +105,30 @@ public class WindowAreaComponentImpl implements WindowAreaComponent, @GuardedBy("mLock") private int mLastReportedRearDisplayPresentationStatus; + @VisibleForTesting + static int getRdmV1Identifier(List<DeviceState> currentSupportedDeviceStates) { + for (int i = 0; i < currentSupportedDeviceStates.size(); i++) { + DeviceState state = currentSupportedDeviceStates.get(i); + if (state.hasProperty(PROPERTY_FEATURE_REAR_DISPLAY) + && !state.hasProperty(PROPERTY_FEATURE_REAR_DISPLAY_OUTER_DEFAULT)) { + return state.getIdentifier(); + } + } + return INVALID_DEVICE_STATE_IDENTIFIER; + } + + @VisibleForTesting + static int getRdmV2Identifier(List<DeviceState> currentSupportedDeviceStates) { + for (int i = 0; i < currentSupportedDeviceStates.size(); i++) { + DeviceState state = currentSupportedDeviceStates.get(i); + if (state.hasProperties(PROPERTY_FEATURE_REAR_DISPLAY, + PROPERTY_FEATURE_REAR_DISPLAY_OUTER_DEFAULT)) { + return state.getIdentifier(); + } + } + return INVALID_DEVICE_STATE_IDENTIFIER; + } + public WindowAreaComponentImpl(@NonNull Context context) { mDeviceStateManager = context.getSystemService(DeviceStateManager.class); mDisplayManager = context.getSystemService(DisplayManager.class); @@ -112,12 +137,10 @@ public class WindowAreaComponentImpl implements WindowAreaComponent, mCurrentSupportedDeviceStates = mDeviceStateManager.getSupportedDeviceStates(); if (Flags.deviceStatePropertyMigration()) { - for (int i = 0; i < mCurrentSupportedDeviceStates.size(); i++) { - DeviceState state = mCurrentSupportedDeviceStates.get(i); - if (state.hasProperty(PROPERTY_FEATURE_REAR_DISPLAY)) { - mRearDisplayState = state.getIdentifier(); - break; - } + if (Flags.deviceStateRdmV2()) { + mRearDisplayState = getRdmV2Identifier(mCurrentSupportedDeviceStates); + } else { + mRearDisplayState = getRdmV1Identifier(mCurrentSupportedDeviceStates); } } else { mFoldedDeviceStates = context.getResources().getIntArray( diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/area/WindowAreaComponentImplTests.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/area/WindowAreaComponentImplTests.java index ccb4ebe9199e..d677fef5c22c 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/area/WindowAreaComponentImplTests.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/area/WindowAreaComponentImplTests.java @@ -16,8 +16,13 @@ package androidx.window.extensions.area; +import static android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_REAR_DISPLAY; +import static android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_REAR_DISPLAY_OUTER_DEFAULT; +import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE_IDENTIFIER; + import static org.junit.Assert.assertEquals; +import android.hardware.devicestate.DeviceState; import android.platform.test.annotations.Presubmit; import android.util.DisplayMetrics; import android.view.Surface; @@ -29,11 +34,34 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + @Presubmit @SmallTest @RunWith(AndroidJUnit4.class) public class WindowAreaComponentImplTests { + private static final DeviceState REAR_DISPLAY_STATE_V1 = new DeviceState( + new DeviceState.Configuration.Builder(1, "STATE_0") + .setSystemProperties( + Set.of(PROPERTY_FEATURE_REAR_DISPLAY)) + .build()); + private static final DeviceState REAR_DISPLAY_STATE_V2 = new DeviceState( + new DeviceState.Configuration.Builder(2, "STATE_0") + .setSystemProperties( + Set.of(PROPERTY_FEATURE_REAR_DISPLAY, + PROPERTY_FEATURE_REAR_DISPLAY_OUTER_DEFAULT)) + .build()); + // The PROPERTY_FEATURE_REAR_DISPLAY_OUTER_DEFAULT state must be present together with the + // PROPERTY_FEATURE_REAR_DISPLAY state in order to be a valid state. + private static final DeviceState INVALID_REAR_DISPLAY_STATE = new DeviceState( + new DeviceState.Configuration.Builder(2, "STATE_0") + .setSystemProperties( + Set.of(PROPERTY_FEATURE_REAR_DISPLAY_OUTER_DEFAULT)) + .build()); + private final DisplayMetrics mTestDisplayMetrics = new DisplayMetrics(); @Before @@ -93,4 +121,37 @@ public class WindowAreaComponentImplTests { Surface.ROTATION_270, Surface.ROTATION_0, mTestDisplayMetrics); assertEquals(expectedMetrics, mTestDisplayMetrics); } + + @Test + public void testRdmV1Identifier() { + final List<DeviceState> supportedStates = new ArrayList<>(); + supportedStates.add(REAR_DISPLAY_STATE_V2); + assertEquals(INVALID_DEVICE_STATE_IDENTIFIER, + WindowAreaComponentImpl.getRdmV1Identifier(supportedStates)); + + supportedStates.add(REAR_DISPLAY_STATE_V1); + assertEquals(REAR_DISPLAY_STATE_V1.getIdentifier(), + WindowAreaComponentImpl.getRdmV1Identifier(supportedStates)); + } + + @Test + public void testRdmV2Identifier_whenStateIsImproperlyConfigured() { + final List<DeviceState> supportedStates = new ArrayList<>(); + supportedStates.add(INVALID_REAR_DISPLAY_STATE); + assertEquals(INVALID_DEVICE_STATE_IDENTIFIER, + WindowAreaComponentImpl.getRdmV2Identifier(supportedStates)); + } + + @Test + public void testRdmV2Identifier_whenStateIsProperlyConfigured() { + final List<DeviceState> supportedStates = new ArrayList<>(); + + supportedStates.add(REAR_DISPLAY_STATE_V1); + assertEquals(INVALID_DEVICE_STATE_IDENTIFIER, + WindowAreaComponentImpl.getRdmV2Identifier(supportedStates)); + + supportedStates.add(REAR_DISPLAY_STATE_V2); + assertEquals(REAR_DISPLAY_STATE_V2.getIdentifier(), + WindowAreaComponentImpl.getRdmV2Identifier(supportedStates)); + } } diff --git a/libs/WindowManager/Shell/AndroidManifest.xml b/libs/WindowManager/Shell/AndroidManifest.xml index b2ac640a468d..636e3cfd571d 100644 --- a/libs/WindowManager/Shell/AndroidManifest.xml +++ b/libs/WindowManager/Shell/AndroidManifest.xml @@ -32,7 +32,6 @@ android:name=".desktopmode.DesktopWallpaperActivity" android:excludeFromRecents="true" android:launchMode="singleInstance" - android:showForAllUsers="true" android:theme="@style/DesktopWallpaperTheme" /> <activity diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt index 0b515f590f98..5f42bb161204 100644 --- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt +++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt @@ -475,6 +475,6 @@ class BubbleStackViewTest { override fun hideCurrentInputMethod() {} - override fun updateBubbleBarLocation(location: BubbleBarLocation) {} + override fun updateBubbleBarLocation(location: BubbleBarLocation, source: Int) {} } } diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt index 0d742cc6e382..6ac36a3319c9 100644 --- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt +++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt @@ -375,7 +375,7 @@ class BubbleBarExpandedViewTest { override fun hideCurrentInputMethod() { } - override fun updateBubbleBarLocation(location: BubbleBarLocation) { + override fun updateBubbleBarLocation(location: BubbleBarLocation, source: Int) { } } } diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerViewTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerViewTest.kt index 00d9a931cebe..0044593ad228 100644 --- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerViewTest.kt +++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerViewTest.kt @@ -351,7 +351,7 @@ class BubbleBarLayerViewTest { override fun hideCurrentInputMethod() {} - override fun updateBubbleBarLocation(location: BubbleBarLocation) {} + override fun updateBubbleBarLocation(location: BubbleBarLocation, source: Int) {} } } diff --git a/libs/WindowManager/Shell/res/layout/bubble_bar_expanded_view.xml b/libs/WindowManager/Shell/res/layout/bubble_bar_expanded_view.xml index 501bedd50f55..c2755ef6ccb6 100644 --- a/libs/WindowManager/Shell/res/layout/bubble_bar_expanded_view.xml +++ b/libs/WindowManager/Shell/res/layout/bubble_bar_expanded_view.xml @@ -19,6 +19,7 @@ android:layout_height="wrap_content" android:layout_width="wrap_content" android:orientation="vertical" + android:clipChildren="false" android:id="@+id/bubble_expanded_view"> <com.android.wm.shell.bubbles.bar.BubbleBarHandleView diff --git a/libs/WindowManager/Shell/res/layout/bubble_bar_menu_view.xml b/libs/WindowManager/Shell/res/layout/bubble_bar_menu_view.xml index f1ecde49ce78..7aca921dccc7 100644 --- a/libs/WindowManager/Shell/res/layout/bubble_bar_menu_view.xml +++ b/libs/WindowManager/Shell/res/layout/bubble_bar_menu_view.xml @@ -14,20 +14,18 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License --> -<com.android.wm.shell.bubbles.bar.BubbleBarMenuView - xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:app="http://schemas.android.com/apk/res-auto" +<com.android.wm.shell.bubbles.bar.BubbleBarMenuView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" + android:clipToPadding="false" android:minWidth="@dimen/bubble_bar_manage_menu_min_width" android:orientation="vertical" - android:elevation="@dimen/bubble_manage_menu_elevation" - android:paddingTop="@dimen/bubble_bar_manage_menu_padding_top" - android:paddingHorizontal="@dimen/bubble_bar_manage_menu_padding" - android:paddingBottom="@dimen/bubble_bar_manage_menu_padding" - android:clipToPadding="false"> + android:visibility="invisible" + tools:visibility="visible"> <LinearLayout android:id="@+id/bubble_bar_manage_menu_bubble_section" diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml index f90e165ffc74..a18a2510f0f7 100644 --- a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml +++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml @@ -168,7 +168,7 @@ </LinearLayout> <LinearLayout - android:id="@+id/open_in_browser_pill" + android:id="@+id/open_in_app_or_browser_pill" android:layout_width="match_parent" android:layout_height="@dimen/desktop_mode_handle_menu_open_in_browser_pill_height" android:layout_marginTop="@dimen/desktop_mode_handle_menu_pill_spacing_margin" @@ -178,7 +178,7 @@ android:background="@drawable/desktop_mode_decor_handle_menu_background"> <Button - android:id="@+id/open_in_browser_button" + android:id="@+id/open_in_app_or_browser_button" android:layout_weight="1" android:contentDescription="@string/open_in_browser_text" android:text="@string/open_in_browser_text" diff --git a/libs/WindowManager/Shell/res/values-af/strings.xml b/libs/WindowManager/Shell/res/values-af/strings.xml index c007c6c7cf57..a4aa3480fb46 100644 --- a/libs/WindowManager/Shell/res/values-af/strings.xml +++ b/libs/WindowManager/Shell/res/values-af/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"Maak in blaaier oop"</string> <string name="new_window_text" msgid="6318648868380652280">"Nuwe venster"</string> <string name="manage_windows_text" msgid="5567366688493093920">"Bestuur vensters"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Verander aspekverhouding"</string> <string name="close_text" msgid="4986518933445178928">"Maak toe"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Maak kieslys toe"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Maak kieslys oop"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"In die app"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"In jou blaaier"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Maak hier apps vinnig in jou blaaier oop"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-am/strings.xml b/libs/WindowManager/Shell/res/values-am/strings.xml index ef4a47ae6163..1cd980460cee 100644 --- a/libs/WindowManager/Shell/res/values-am/strings.xml +++ b/libs/WindowManager/Shell/res/values-am/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"በአሳሽ ውስጥ ክፈት"</string> <string name="new_window_text" msgid="6318648868380652280">"አዲስ መስኮት"</string> <string name="manage_windows_text" msgid="5567366688493093920">"መስኮቶችን አስተዳድር"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"ምጥጥነ ገፅታ ለውጥ"</string> <string name="close_text" msgid="4986518933445178928">"ዝጋ"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"ምናሌ ዝጋ"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"ምናሌን ክፈት"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"በመተግበሪያው ውስጥ"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"በአሳሽዎ ውስጥ"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"እሺ"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"እዚህ አሳሽዎ ውስጥ መተግበሪያዎችን በፍጥነት ይክፈቱ"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-ar/strings.xml b/libs/WindowManager/Shell/res/values-ar/strings.xml index 7ddd4d17ec37..41ebfcd0ee85 100644 --- a/libs/WindowManager/Shell/res/values-ar/strings.xml +++ b/libs/WindowManager/Shell/res/values-ar/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"فتح في المتصفِّح"</string> <string name="new_window_text" msgid="6318648868380652280">"نافذة جديدة"</string> <string name="manage_windows_text" msgid="5567366688493093920">"إدارة النوافذ"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"تغيير نسبة العرض إلى الارتفاع"</string> <string name="close_text" msgid="4986518933445178928">"إغلاق"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"إغلاق القائمة"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"فتح القائمة"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"في التطبيق"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"في المتصفِّح"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"حسنًا"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"فتح التطبيقات بسرعة في المتصفِّح هنا"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-as/strings.xml b/libs/WindowManager/Shell/res/values-as/strings.xml index 85cf31fd4d20..203fed0aecef 100644 --- a/libs/WindowManager/Shell/res/values-as/strings.xml +++ b/libs/WindowManager/Shell/res/values-as/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"ব্ৰাউজাৰত খোলক"</string> <string name="new_window_text" msgid="6318648868380652280">"নতুন ৱিণ্ড’"</string> <string name="manage_windows_text" msgid="5567366688493093920">"ৱিণ্ড’ পৰিচালনা কৰক"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"আকাৰৰ অনুপাত সলনি কৰক"</string> <string name="close_text" msgid="4986518933445178928">"বন্ধ কৰক"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"মেনু বন্ধ কৰক"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"মেনু খোলক"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"এপ্টোত"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"আপোনাৰ ব্ৰাউজাৰত"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"ঠিক আছে"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"ইয়াত আপোনাৰ ব্ৰাউজাৰত ক্ষিপ্ৰভাৱে এপ্ খোলক"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-az/strings.xml b/libs/WindowManager/Shell/res/values-az/strings.xml index d27607a8c8df..31ddc9b78b68 100644 --- a/libs/WindowManager/Shell/res/values-az/strings.xml +++ b/libs/WindowManager/Shell/res/values-az/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"Brauzerdə açın"</string> <string name="new_window_text" msgid="6318648868380652280">"Yeni pəncərə"</string> <string name="manage_windows_text" msgid="5567366688493093920">"Pəncərələri idarə edin"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Tərəflər nisbətini dəyişin"</string> <string name="close_text" msgid="4986518933445178928">"Bağlayın"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Menyunu bağlayın"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Menyunu açın"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Tətbiqdə"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Brauzerinizdə"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Brauzerinizdəki tətbiqləri burada sürətlə açın"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml index f251791538dc..486b3cfbfee4 100644 --- a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml +++ b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"Otvorite u pregledaču"</string> <string name="new_window_text" msgid="6318648868380652280">"Novi prozor"</string> <string name="manage_windows_text" msgid="5567366688493093920">"Upravljajte prozorima"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Promenite razmeru"</string> <string name="close_text" msgid="4986518933445178928">"Zatvorite"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Zatvorite meni"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Otvorite meni"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"U aplikaciji"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"U pregledaču"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Potvrdi"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Ovde možete brzo da otvarate aplikacije u pregledaču"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-be/strings.xml b/libs/WindowManager/Shell/res/values-be/strings.xml index 3393af65f268..cc42da947c36 100644 --- a/libs/WindowManager/Shell/res/values-be/strings.xml +++ b/libs/WindowManager/Shell/res/values-be/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"Адкрыць у браўзеры"</string> <string name="new_window_text" msgid="6318648868380652280">"Новае акно"</string> <string name="manage_windows_text" msgid="5567366688493093920">"Кіраваць вокнамі"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Змяніць суадносіны бакоў"</string> <string name="close_text" msgid="4986518933445178928">"Закрыць"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Закрыць меню"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Адкрыць меню"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"У праграме"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"У браўзеры"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"ОК"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Тут можна хутка адкрываць праграмы ў браўзеры"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-bg/strings.xml b/libs/WindowManager/Shell/res/values-bg/strings.xml index c2236a0cfe82..c12b37b34d5a 100644 --- a/libs/WindowManager/Shell/res/values-bg/strings.xml +++ b/libs/WindowManager/Shell/res/values-bg/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"Отваряне в браузър"</string> <string name="new_window_text" msgid="6318648868380652280">"Нов прозорец"</string> <string name="manage_windows_text" msgid="5567366688493093920">"Управление на прозорците"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Промяна на съотношението"</string> <string name="close_text" msgid="4986518933445178928">"Затваряне"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Затваряне на менюто"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Отваряне на менюто"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"В приложението"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"В браузъра ви"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Бързо отваряйте приложения в браузъра си оттук"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-bn/strings.xml b/libs/WindowManager/Shell/res/values-bn/strings.xml index ae302665263c..aca5b34ae4c0 100644 --- a/libs/WindowManager/Shell/res/values-bn/strings.xml +++ b/libs/WindowManager/Shell/res/values-bn/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"ব্রাউজারে খুলুন"</string> <string name="new_window_text" msgid="6318648868380652280">"নতুন উইন্ডো"</string> <string name="manage_windows_text" msgid="5567366688493093920">"উইন্ডো ম্যানেজ করুন"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"অ্যাস্পেক্ট রেশিও পরিবর্তন করুন"</string> <string name="close_text" msgid="4986518933445178928">"বন্ধ করুন"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"\'মেনু\' বন্ধ করুন"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"মেনু খুলুন"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"অ্যাপের মধ্যে"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"আপনার ব্রাউজারে"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"ঠিক আছে"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"এখানে আপনার ব্রাউজারে দ্রুত অ্যাপ খুলুন"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-bs/strings.xml b/libs/WindowManager/Shell/res/values-bs/strings.xml index 3a1512f4fa29..6bd6473a5f13 100644 --- a/libs/WindowManager/Shell/res/values-bs/strings.xml +++ b/libs/WindowManager/Shell/res/values-bs/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"Otvaranje u pregledniku"</string> <string name="new_window_text" msgid="6318648868380652280">"Novi prozor"</string> <string name="manage_windows_text" msgid="5567366688493093920">"Upravljanje prozorima"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Promjena formata slike"</string> <string name="close_text" msgid="4986518933445178928">"Zatvaranje"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Zatvaranje menija"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Otvaranje menija"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"U aplikaciji"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"U pregledniku"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Uredu"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Ovdje možete brzo otvarati aplikacije u pregledniku"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-ca/strings.xml b/libs/WindowManager/Shell/res/values-ca/strings.xml index 3992387584ce..d9ad5a68d163 100644 --- a/libs/WindowManager/Shell/res/values-ca/strings.xml +++ b/libs/WindowManager/Shell/res/values-ca/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"Obre al navegador"</string> <string name="new_window_text" msgid="6318648868380652280">"Finestra nova"</string> <string name="manage_windows_text" msgid="5567366688493093920">"Gestiona les finestres"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Canvia la relació d\'aspecte"</string> <string name="close_text" msgid="4986518933445178928">"Tanca"</string> <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> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"A l\'aplicació"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Al navegador"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"D\'acord"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Obre ràpidament aplicacions al navegador aquí"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-cs/strings.xml b/libs/WindowManager/Shell/res/values-cs/strings.xml index 9d5ec766f67d..ab51b666cdda 100644 --- a/libs/WindowManager/Shell/res/values-cs/strings.xml +++ b/libs/WindowManager/Shell/res/values-cs/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"Otevřít v prohlížeči"</string> <string name="new_window_text" msgid="6318648868380652280">"Nové okno"</string> <string name="manage_windows_text" msgid="5567366688493093920">"Spravovat okna"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Změnit poměr stran"</string> <string name="close_text" msgid="4986518933445178928">"Zavřít"</string> <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> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"V aplikaci"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"V prohlížeči"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Odtud můžete v prohlížeči rychle otevírat aplikace"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-da/strings.xml b/libs/WindowManager/Shell/res/values-da/strings.xml index 91a294d43dfb..443620804e10 100644 --- a/libs/WindowManager/Shell/res/values-da/strings.xml +++ b/libs/WindowManager/Shell/res/values-da/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"Åbn i browser"</string> <string name="new_window_text" msgid="6318648868380652280">"Nyt vindue"</string> <string name="manage_windows_text" msgid="5567366688493093920">"Administrer vinduer"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Skift billedformat"</string> <string name="close_text" msgid="4986518933445178928">"Luk"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Luk menu"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Åbn menu"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"I appen"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"I din browser"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Åbn hurtigt apps i din browser her"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-de/strings.xml b/libs/WindowManager/Shell/res/values-de/strings.xml index 9004b19e7607..b6e89c0eeb8e 100644 --- a/libs/WindowManager/Shell/res/values-de/strings.xml +++ b/libs/WindowManager/Shell/res/values-de/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"Im Browser öffnen"</string> <string name="new_window_text" msgid="6318648868380652280">"Neues Fenster"</string> <string name="manage_windows_text" msgid="5567366688493093920">"Fenster verwalten"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Seitenverhältnis ändern"</string> <string name="close_text" msgid="4986518933445178928">"Schließen"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Menü schließen"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Menü öffnen"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"In der App"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"In deinem Browser"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Ok"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Hier kannst du Apps schnell in deinem Browser öffnen"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml index 85d5646ca26f..fd6317530109 100644 --- a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml +++ b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"Open in browser"</string> <string name="new_window_text" msgid="6318648868380652280">"New window"</string> <string name="manage_windows_text" msgid="5567366688493093920">"Manage windows"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Change aspect ratio"</string> <string name="close_text" msgid="4986518933445178928">"Close"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Close menu"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Open menu"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"In the app"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"In your browser"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Quickly open apps in your browser here"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml index 85d5646ca26f..fd6317530109 100644 --- a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml +++ b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"Open in browser"</string> <string name="new_window_text" msgid="6318648868380652280">"New window"</string> <string name="manage_windows_text" msgid="5567366688493093920">"Manage windows"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Change aspect ratio"</string> <string name="close_text" msgid="4986518933445178928">"Close"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Close menu"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Open menu"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"In the app"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"In your browser"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Quickly open apps in your browser here"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml index 85d5646ca26f..fd6317530109 100644 --- a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml +++ b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"Open in browser"</string> <string name="new_window_text" msgid="6318648868380652280">"New window"</string> <string name="manage_windows_text" msgid="5567366688493093920">"Manage windows"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Change aspect ratio"</string> <string name="close_text" msgid="4986518933445178928">"Close"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Close menu"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Open menu"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"In the app"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"In your browser"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Quickly open apps in your browser here"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml index f6a9b6d8799a..e67fc8e2c63c 100644 --- a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml +++ b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"Abrir en el navegador"</string> <string name="new_window_text" msgid="6318648868380652280">"Nueva ventana"</string> <string name="manage_windows_text" msgid="5567366688493093920">"Administrar ventanas"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Cambiar relación de aspecto"</string> <string name="close_text" msgid="4986518933445178928">"Cerrar"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Cerrar menú"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Abrir el menú"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"En la app"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"En un navegador"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Aceptar"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Abre rápidamente apps en tu navegador aquí"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-es/strings.xml b/libs/WindowManager/Shell/res/values-es/strings.xml index 2c902903039c..2f5ec64be629 100644 --- a/libs/WindowManager/Shell/res/values-es/strings.xml +++ b/libs/WindowManager/Shell/res/values-es/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"Abrir en el navegador"</string> <string name="new_window_text" msgid="6318648868380652280">"Ventana nueva"</string> <string name="manage_windows_text" msgid="5567366688493093920">"Gestionar ventanas"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Cambiar relación de aspecto"</string> <string name="close_text" msgid="4986518933445178928">"Cerrar"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Cerrar menú"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Abrir menú"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"En la aplicación"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"En el navegador"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Aceptar"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Abre rápidamente aplicaciones en tu navegador aquí"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-et/strings.xml b/libs/WindowManager/Shell/res/values-et/strings.xml index 3d1977bd12f0..dd78628093d4 100644 --- a/libs/WindowManager/Shell/res/values-et/strings.xml +++ b/libs/WindowManager/Shell/res/values-et/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"Avamine brauseris"</string> <string name="new_window_text" msgid="6318648868380652280">"Uus aken"</string> <string name="manage_windows_text" msgid="5567366688493093920">"Akende haldamine"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Kuvasuhte muutmine"</string> <string name="close_text" msgid="4986518933445178928">"Sule"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Sule menüü"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Ava menüü"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Rakenduses"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Brauseris"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Avage rakendusi kiiresti oma brauseris siin"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-eu/strings.xml b/libs/WindowManager/Shell/res/values-eu/strings.xml index 2e1822d30ba2..1cfc69457ce9 100644 --- a/libs/WindowManager/Shell/res/values-eu/strings.xml +++ b/libs/WindowManager/Shell/res/values-eu/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"Ireki arakatzailean"</string> <string name="new_window_text" msgid="6318648868380652280">"Leiho berria"</string> <string name="manage_windows_text" msgid="5567366688493093920">"Kudeatu leihoak"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Aldatu aspektu-erlazioa"</string> <string name="close_text" msgid="4986518933445178928">"Itxi"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Itxi menua"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Ireki menua"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Aplikazioan"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Arakatzailean"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Ados"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Ireki aplikazioak arakatzailean bizkor"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-fa/strings.xml b/libs/WindowManager/Shell/res/values-fa/strings.xml index b14a1fff98a3..f76f67d2e796 100644 --- a/libs/WindowManager/Shell/res/values-fa/strings.xml +++ b/libs/WindowManager/Shell/res/values-fa/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"باز کردن در مرورگر"</string> <string name="new_window_text" msgid="6318648868380652280">"پنجره جدید"</string> <string name="manage_windows_text" msgid="5567366688493093920">"مدیریت کردن پنجرهها"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"تغییر نسبت ابعادی"</string> <string name="close_text" msgid="4986518933445178928">"بستن"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"بستن منو"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"باز کردن منو"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"در برنامه"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"در مرورگر"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"تأیید"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"در اینجا سریع برنامهها را در مرورگرتان باز کنید"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-fi/strings.xml b/libs/WindowManager/Shell/res/values-fi/strings.xml index 53b22ab96bf8..a1ec0150ffae 100644 --- a/libs/WindowManager/Shell/res/values-fi/strings.xml +++ b/libs/WindowManager/Shell/res/values-fi/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"Avaa selaimessa"</string> <string name="new_window_text" msgid="6318648868380652280">"Uusi ikkuna"</string> <string name="manage_windows_text" msgid="5567366688493093920">"Hallinnoi ikkunoita"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Vaihda kuvasuhdetta"</string> <string name="close_text" msgid="4986518933445178928">"Sulje"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Sulje valikko"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Avaa valikko"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Sovelluksessa"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Selaimella"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Avaa sovellukset nopeasti selaimessa täältä"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml index 36fc2c22c156..1b9b74a45671 100644 --- a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml +++ b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"Ouvrir dans le navigateur"</string> <string name="new_window_text" msgid="6318648868380652280">"Nouvelle fenêtre"</string> <string name="manage_windows_text" msgid="5567366688493093920">"Gérer les fenêtres"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Modifier les proportions"</string> <string name="close_text" msgid="4986518933445178928">"Fermer"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Fermer le menu"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Ouvrir le menu"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Dans l\'appli"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Dans votre navigateur"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Ouvrez rapidement des applis dans votre navigateur ici"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-fr/strings.xml b/libs/WindowManager/Shell/res/values-fr/strings.xml index 6c475a8c9a12..7e0a0b1acb7b 100644 --- a/libs/WindowManager/Shell/res/values-fr/strings.xml +++ b/libs/WindowManager/Shell/res/values-fr/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"Ouvrir dans un navigateur"</string> <string name="new_window_text" msgid="6318648868380652280">"Nouvelle fenêtre"</string> <string name="manage_windows_text" msgid="5567366688493093920">"Gérer les fenêtres"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Modifier le format"</string> <string name="close_text" msgid="4986518933445178928">"Fermer"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Fermer le menu"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Ouvrir le menu"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Dans l\'application"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Dans votre navigateur"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Ouvrez rapidement des applications dans votre navigateur ici"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-gl/strings.xml b/libs/WindowManager/Shell/res/values-gl/strings.xml index c78cba6a0d4e..bdd07476efcf 100644 --- a/libs/WindowManager/Shell/res/values-gl/strings.xml +++ b/libs/WindowManager/Shell/res/values-gl/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"Abrir no navegador"</string> <string name="new_window_text" msgid="6318648868380652280">"Ventá nova"</string> <string name="manage_windows_text" msgid="5567366688493093920">"Xestionar as ventás"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Cambiar a proporción"</string> <string name="close_text" msgid="4986518933445178928">"Pechar"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Pechar o menú"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Abrir o menú"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Na aplicación"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"No navegador"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Aceptar"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Abre rapidamente aplicacións no navegador aquí"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-gu/strings.xml b/libs/WindowManager/Shell/res/values-gu/strings.xml index 16188cb6e5fb..d23c4fd8f0e0 100644 --- a/libs/WindowManager/Shell/res/values-gu/strings.xml +++ b/libs/WindowManager/Shell/res/values-gu/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"બ્રાઉઝરમાં ખોલો"</string> <string name="new_window_text" msgid="6318648868380652280">"નવી વિન્ડો"</string> <string name="manage_windows_text" msgid="5567366688493093920">"વિન્ડો મેનેજ કરો"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"સાપેક્ષ ગુણોત્તર બદલો"</string> <string name="close_text" msgid="4986518933445178928">"બંધ કરો"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"મેનૂ બંધ કરો"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"મેનૂ ખોલો"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"ઍપમાં"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"તમારા બ્રાઉઝરમાં"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"ઓકે"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"તમારા બ્રાઉઝરમાં અહીં ઝડપથી ઍપ ખોલો"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-hi/strings.xml b/libs/WindowManager/Shell/res/values-hi/strings.xml index 51ca24cbcfdb..4eec6f877fab 100644 --- a/libs/WindowManager/Shell/res/values-hi/strings.xml +++ b/libs/WindowManager/Shell/res/values-hi/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"ब्राउज़र में खोलें"</string> <string name="new_window_text" msgid="6318648868380652280">"नई विंडो"</string> <string name="manage_windows_text" msgid="5567366688493093920">"विंडो मैनेज करें"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"आसपेक्ट रेशियो (लंबाई-चौड़ाई का अनुपात) बदलें"</string> <string name="close_text" msgid="4986518933445178928">"बंद करें"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"मेन्यू बंद करें"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"मेन्यू खोलें"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"ऐप्लिकेशन में"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"आपके ब्राउज़र में"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"ठीक है"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"अपने ब्राउज़र में तुरंत ऐप्लिकेशन खोलें"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-hr/strings.xml b/libs/WindowManager/Shell/res/values-hr/strings.xml index 70ecca8950d9..a119d9e7f782 100644 --- a/libs/WindowManager/Shell/res/values-hr/strings.xml +++ b/libs/WindowManager/Shell/res/values-hr/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"Otvori u pregledniku"</string> <string name="new_window_text" msgid="6318648868380652280">"Novi prozor"</string> <string name="manage_windows_text" msgid="5567366688493093920">"Upravljanje prozorima"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Promijeni omjer slike"</string> <string name="close_text" msgid="4986518933445178928">"Zatvorite"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Zatvorite izbornik"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Otvaranje izbornika"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"U aplikaciji"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"U pregledniku"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"U redu"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Ovdje brzo otvorite aplikacije u pregledniku"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-hu/strings.xml b/libs/WindowManager/Shell/res/values-hu/strings.xml index a46c14f6f712..c07b6c3b9f1d 100644 --- a/libs/WindowManager/Shell/res/values-hu/strings.xml +++ b/libs/WindowManager/Shell/res/values-hu/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"Megnyitás böngészőben"</string> <string name="new_window_text" msgid="6318648868380652280">"Új ablak"</string> <string name="manage_windows_text" msgid="5567366688493093920">"Ablakok kezelése"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Méretarány módosítása"</string> <string name="close_text" msgid="4986518933445178928">"Bezárás"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Menü bezárása"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Menü megnyitása"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Az alkalmazásban"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"A böngészőben"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Itt gyorsan megnyithatja az alkalmazásokat a böngészőben"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-hy/strings.xml b/libs/WindowManager/Shell/res/values-hy/strings.xml index b7105c989ce4..52eb18580de1 100644 --- a/libs/WindowManager/Shell/res/values-hy/strings.xml +++ b/libs/WindowManager/Shell/res/values-hy/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"Բացել դիտարկիչում"</string> <string name="new_window_text" msgid="6318648868380652280">"Նոր պատուհան"</string> <string name="manage_windows_text" msgid="5567366688493093920">"Կառավարել պատուհանները"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Փոխել կողմերի հարաբերակցությունը"</string> <string name="close_text" msgid="4986518933445178928">"Փակել"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Փակել ընտրացանկը"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Բացել ընտրացանկը"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Հավելվածում"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Ձեր դիտարկիչում"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Եղավ"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Արագ բացեք հավելվածները ձեր դիտարկիչում"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-in/strings.xml b/libs/WindowManager/Shell/res/values-in/strings.xml index 4a40027639fd..f8f9d5e16439 100644 --- a/libs/WindowManager/Shell/res/values-in/strings.xml +++ b/libs/WindowManager/Shell/res/values-in/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"Buka di browser"</string> <string name="new_window_text" msgid="6318648868380652280">"Jendela Baru"</string> <string name="manage_windows_text" msgid="5567366688493093920">"Kelola Jendela"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Ubah rasio aspek"</string> <string name="close_text" msgid="4986518933445178928">"Tutup"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Tutup Menu"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Buka Menu"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Di aplikasi"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Di browser Anda"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Oke"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Buka aplikasi di browser Anda dengan cepat di sini"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-is/strings.xml b/libs/WindowManager/Shell/res/values-is/strings.xml index d3f2c3dff0ee..8a9e3c0ca0a4 100644 --- a/libs/WindowManager/Shell/res/values-is/strings.xml +++ b/libs/WindowManager/Shell/res/values-is/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"Opna í vafra"</string> <string name="new_window_text" msgid="6318648868380652280">"Nýr gluggi"</string> <string name="manage_windows_text" msgid="5567366688493093920">"Stjórna gluggum"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Breyta myndhlutfalli"</string> <string name="close_text" msgid="4986518933445178928">"Loka"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Loka valmynd"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Opna valmynd"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Í forritinu"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Í vafranum"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Í lagi"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Opna forrit fljótt í vafranum þínum hér"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-it/strings.xml b/libs/WindowManager/Shell/res/values-it/strings.xml index e4f78addf432..138adefed160 100644 --- a/libs/WindowManager/Shell/res/values-it/strings.xml +++ b/libs/WindowManager/Shell/res/values-it/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"Apri nel browser"</string> <string name="new_window_text" msgid="6318648868380652280">"Nuova finestra"</string> <string name="manage_windows_text" msgid="5567366688493093920">"Gestisci finestre"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Cambia proporzioni"</string> <string name="close_text" msgid="4986518933445178928">"Chiudi"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Chiudi il menu"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Apri il menu"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"All\'interno dell\'app"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Nel browser"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Ok"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Apri rapidamente le app nel browser qui"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-iw/strings.xml b/libs/WindowManager/Shell/res/values-iw/strings.xml index b042ee491860..917738dc1575 100644 --- a/libs/WindowManager/Shell/res/values-iw/strings.xml +++ b/libs/WindowManager/Shell/res/values-iw/strings.xml @@ -109,7 +109,7 @@ <string name="letterbox_restart_dialog_description" msgid="6096946078246557848">"אפשר להפעיל מחדש את האפליקציה כדי שהיא תוצג באופן טוב יותר במסך, אבל ייתכן שההתקדמות שלך או כל שינוי שלא נשמר יאבדו"</string> <string name="letterbox_restart_cancel" msgid="1342209132692537805">"ביטול"</string> <string name="letterbox_restart_restart" msgid="8529976234412442973">"הפעלה מחדש"</string> - <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"אין להציג שוב"</string> + <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"לא להציג שוב"</string> <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"אפשר להקיש הקשה כפולה כדי\nלהעביר את האפליקציה למקום אחר"</string> <string name="maximize_button_text" msgid="1650859196290301963">"הגדלה"</string> <string name="minimize_button_text" msgid="271592547935841753">"מזעור"</string> @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"פתיחה בדפדפן"</string> <string name="new_window_text" msgid="6318648868380652280">"חלון חדש"</string> <string name="manage_windows_text" msgid="5567366688493093920">"ניהול החלונות"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"שינוי של יחס גובה-רוחב"</string> <string name="close_text" msgid="4986518933445178928">"סגירה"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"סגירת התפריט"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"פתיחת התפריט"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"באפליקציה"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"בדפדפן"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"אישור"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"כאן אפשר לפתוח אפליקציות בדפדפן במהירות"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-ja/strings.xml b/libs/WindowManager/Shell/res/values-ja/strings.xml index 6d5954f755c4..35c48212cf39 100644 --- a/libs/WindowManager/Shell/res/values-ja/strings.xml +++ b/libs/WindowManager/Shell/res/values-ja/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"ブラウザで開く"</string> <string name="new_window_text" msgid="6318648868380652280">"新しいウィンドウ"</string> <string name="manage_windows_text" msgid="5567366688493093920">"ウィンドウを管理する"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"アスペクト比を変更"</string> <string name="close_text" msgid="4986518933445178928">"閉じる"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"メニューを閉じる"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"メニューを開く"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"アプリ内"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"ブラウザ内"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"ブラウザでアプリをすばやく開けます"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-ka/strings.xml b/libs/WindowManager/Shell/res/values-ka/strings.xml index f38f68fbc657..9b9966f152ee 100644 --- a/libs/WindowManager/Shell/res/values-ka/strings.xml +++ b/libs/WindowManager/Shell/res/values-ka/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"ბრაუზერში გახსნა"</string> <string name="new_window_text" msgid="6318648868380652280">"ახალი ფანჯარა"</string> <string name="manage_windows_text" msgid="5567366688493093920">"ფანჯრების მართვა"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"თანაფარდობის შეცვლა"</string> <string name="close_text" msgid="4986518933445178928">"დახურვა"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"მენიუს დახურვა"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"მენიუს გახსნა"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"აპში"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"თქვენს ბრაუზერში"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"კარგი"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"სწრაფად გახსენით აპები თქვენს ბრაუზერში აქ"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-kk/strings.xml b/libs/WindowManager/Shell/res/values-kk/strings.xml index b4be01ab1933..8618ba9b2b0f 100644 --- a/libs/WindowManager/Shell/res/values-kk/strings.xml +++ b/libs/WindowManager/Shell/res/values-kk/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"Браузерден ашу"</string> <string name="new_window_text" msgid="6318648868380652280">"Жаңа терезе"</string> <string name="manage_windows_text" msgid="5567366688493093920">"Терезелерді басқару"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Арақатынасты өзгерту"</string> <string name="close_text" msgid="4986518933445178928">"Жабу"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Мәзірді жабу"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Мәзірді ашу"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Қолданбада"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Браузерде"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Жарайды"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Осындағы браузерде қолданбаларды жылдам ашуға болады."</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-km/strings.xml b/libs/WindowManager/Shell/res/values-km/strings.xml index 2424a420f375..7f853f3e1e2f 100644 --- a/libs/WindowManager/Shell/res/values-km/strings.xml +++ b/libs/WindowManager/Shell/res/values-km/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"បើកក្នុងកម្មវិធីរុករកតាមអ៊ីនធឺណិត"</string> <string name="new_window_text" msgid="6318648868380652280">"វិនដូថ្មី"</string> <string name="manage_windows_text" msgid="5567366688493093920">"គ្រប់គ្រងវិនដូ"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"ប្ដូរសមាមាត្រ"</string> <string name="close_text" msgid="4986518933445178928">"បិទ"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"បិទម៉ឺនុយ"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"បើកម៉ឺនុយ"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"នៅក្នុងកម្មវិធី"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"នៅក្នុងកម្មវិធីរុករកតាមអ៊ីនធឺណិតរបស់អ្នក"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"យល់ព្រម"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"បើកកម្មវិធីយ៉ាងរហ័សនៅក្នុងកម្មវិធីរុករកតាមអ៊ីនធឺណិតរបស់អ្នកនៅទីនេះ"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-kn/strings.xml b/libs/WindowManager/Shell/res/values-kn/strings.xml index 56a5d641be01..456dea2fdb0f 100644 --- a/libs/WindowManager/Shell/res/values-kn/strings.xml +++ b/libs/WindowManager/Shell/res/values-kn/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"ಬ್ರೌಸರ್ನಲ್ಲಿ ತೆರೆಯಿರಿ"</string> <string name="new_window_text" msgid="6318648868380652280">"ಹೊಸ ವಿಂಡೋ"</string> <string name="manage_windows_text" msgid="5567366688493093920">"ವಿಂಡೋಗಳನ್ನು ನಿರ್ವಹಿಸಿ"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"ದೃಶ್ಯಾನುಪಾತವನ್ನು ಬದಲಾಯಿಸಿ"</string> <string name="close_text" msgid="4986518933445178928">"ಮುಚ್ಚಿ"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"ಮೆನು ಮುಚ್ಚಿ"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"ಮೆನು ತೆರೆಯಿರಿ"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"ಆ್ಯಪ್ನಲ್ಲಿ"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"ನಿಮ್ಮ ಬ್ರೌಸರ್ನಲ್ಲಿ"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"ಸರಿ"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"ಇಲ್ಲಿಂದ ನಿಮ್ಮ ಬ್ರೌಸರ್ನಲ್ಲಿ ಆ್ಯಪ್ಗಳನ್ನು ತ್ವರಿತವಾಗಿ ತೆರೆಯಿರಿ"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-ko/strings.xml b/libs/WindowManager/Shell/res/values-ko/strings.xml index d45d51520192..763cda738541 100644 --- a/libs/WindowManager/Shell/res/values-ko/strings.xml +++ b/libs/WindowManager/Shell/res/values-ko/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"브라우저에서 열기"</string> <string name="new_window_text" msgid="6318648868380652280">"새 창"</string> <string name="manage_windows_text" msgid="5567366688493093920">"창 관리"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"가로세로 비율 변경"</string> <string name="close_text" msgid="4986518933445178928">"닫기"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"메뉴 닫기"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"메뉴 열기"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"앱에서"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"브라우저에서"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"확인"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"이 브라우저에서 앱을 빠르게 여세요."</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-ky/strings.xml b/libs/WindowManager/Shell/res/values-ky/strings.xml index 501b2a73351b..bffc3b1d11a8 100644 --- a/libs/WindowManager/Shell/res/values-ky/strings.xml +++ b/libs/WindowManager/Shell/res/values-ky/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"Серепчиден ачуу"</string> <string name="new_window_text" msgid="6318648868380652280">"Жаңы терезе"</string> <string name="manage_windows_text" msgid="5567366688493093920">"Терезелерди тескөө"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Тараптардын катнашын өзгөртүү"</string> <string name="close_text" msgid="4986518933445178928">"Жабуу"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Менюну жабуу"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Менюну ачуу"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Колдонмодо"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Серепчиңизде"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Жарайт"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Бул жерде серепчиңизден колдонмолорду тез ачасыз"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-lt/strings.xml b/libs/WindowManager/Shell/res/values-lt/strings.xml index 7e1aac00f404..d7a907cdc105 100644 --- a/libs/WindowManager/Shell/res/values-lt/strings.xml +++ b/libs/WindowManager/Shell/res/values-lt/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"Atidaryti naršyklėje"</string> <string name="new_window_text" msgid="6318648868380652280">"Naujas langas"</string> <string name="manage_windows_text" msgid="5567366688493093920">"Tvarkyti langus"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Keisti kraštinių santykį"</string> <string name="close_text" msgid="4986518933445178928">"Uždaryti"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Uždaryti meniu"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Atidaryti meniu"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Programoje"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Naršyklėje"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Gerai"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Čia greitai atidarykite programas naršyklėje"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-lv/strings.xml b/libs/WindowManager/Shell/res/values-lv/strings.xml index 1ad0454e2931..4ba7c2346a33 100644 --- a/libs/WindowManager/Shell/res/values-lv/strings.xml +++ b/libs/WindowManager/Shell/res/values-lv/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"Atvērt pārlūkā"</string> <string name="new_window_text" msgid="6318648868380652280">"Jauns logs"</string> <string name="manage_windows_text" msgid="5567366688493093920">"Pārvaldīt logus"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Mainīt malu attiecību"</string> <string name="close_text" msgid="4986518933445178928">"Aizvērt"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Aizvērt izvēlni"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Atvērt izvēlni"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Lietotnē"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Pārlūkprogrammā"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Labi"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"No šejienes varat ātri atvērt lietotnes savā pārlūkprogrammā"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-mk/strings.xml b/libs/WindowManager/Shell/res/values-mk/strings.xml index 444372e00656..d20eba55eac9 100644 --- a/libs/WindowManager/Shell/res/values-mk/strings.xml +++ b/libs/WindowManager/Shell/res/values-mk/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"Отвори во прелистувач"</string> <string name="new_window_text" msgid="6318648868380652280">"Нов прозорец"</string> <string name="manage_windows_text" msgid="5567366688493093920">"Управувајте со прозорци"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Промени го соодносот"</string> <string name="close_text" msgid="4986518933445178928">"Затворете"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Затворете го менито"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Отвори го менито"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Во апликацијата"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Во прелистувачот"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Во ред"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Брзо отворајте ги апликациите во вашиот прелистувач овде"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-ml/strings.xml b/libs/WindowManager/Shell/res/values-ml/strings.xml index cd2f63543970..81c5094526c1 100644 --- a/libs/WindowManager/Shell/res/values-ml/strings.xml +++ b/libs/WindowManager/Shell/res/values-ml/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"ബ്രൗസറിൽ തുറക്കുക"</string> <string name="new_window_text" msgid="6318648868380652280">"പുതിയ വിന്ഡോ"</string> <string name="manage_windows_text" msgid="5567366688493093920">"വിൻഡോകൾ മാനേജ് ചെയ്യുക"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"വീക്ഷണ അനുപാതം മാറ്റുക"</string> <string name="close_text" msgid="4986518933445178928">"അടയ്ക്കുക"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"മെനു അടയ്ക്കുക"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"മെനു തുറക്കുക"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"ആപ്പിൽ"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"നിങ്ങളുടെ ബ്രൗസറിൽ"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"ശരി"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"നിങ്ങളുടെ ബ്രൗസറിലെ ആപ്പുകൾ ഇവിടെ അതിവേഗം തുറക്കുക"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-mn/strings.xml b/libs/WindowManager/Shell/res/values-mn/strings.xml index 1bec2870e853..35da93e774b5 100644 --- a/libs/WindowManager/Shell/res/values-mn/strings.xml +++ b/libs/WindowManager/Shell/res/values-mn/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"Хөтчид нээх"</string> <string name="new_window_text" msgid="6318648868380652280">"Шинэ цонх"</string> <string name="manage_windows_text" msgid="5567366688493093920">"Windows-г удирдах"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Харьцааг өөрчлөх"</string> <string name="close_text" msgid="4986518933445178928">"Хаах"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Цэсийг хаах"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Цэсийг нээх"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Аппад"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Хөтчидөө"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Эндээс хөтчидөө аппуудыг шуурхай нээгээрэй"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-mr/strings.xml b/libs/WindowManager/Shell/res/values-mr/strings.xml index f36c40fbbe6c..c6b874a6780b 100644 --- a/libs/WindowManager/Shell/res/values-mr/strings.xml +++ b/libs/WindowManager/Shell/res/values-mr/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"ब्राउझरमध्ये उघडा"</string> <string name="new_window_text" msgid="6318648868380652280">"नवीन विंडो"</string> <string name="manage_windows_text" msgid="5567366688493093920">"विंडो व्यवस्थापित करा"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"आस्पेक्ट रेशो बदला"</string> <string name="close_text" msgid="4986518933445178928">"बंद करा"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"मेनू बंद करा"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"मेनू उघडा"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"ॲपमध्ये"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"तुमच्या ब्राउझरमध्ये"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"ओके"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"इथे तुमच्या ब्राउझरमध्ये अॅप्स झटपट उघडा"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-ms/strings.xml b/libs/WindowManager/Shell/res/values-ms/strings.xml index 6b4361aa1636..0fce0e9da45f 100644 --- a/libs/WindowManager/Shell/res/values-ms/strings.xml +++ b/libs/WindowManager/Shell/res/values-ms/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"Buka dalam penyemak imbas"</string> <string name="new_window_text" msgid="6318648868380652280">"Tetingkap Baharu"</string> <string name="manage_windows_text" msgid="5567366688493093920">"Urus Tetingkap"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Tukar nisbah bidang"</string> <string name="close_text" msgid="4986518933445178928">"Tutup"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Tutup Menu"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Buka Menu"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Pada apl"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Pada penyemak imbas"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Buka apl dengan pantas dalam penyemak imbas anda di sini"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-my/strings.xml b/libs/WindowManager/Shell/res/values-my/strings.xml index d47c196ae469..abc2a19ba989 100644 --- a/libs/WindowManager/Shell/res/values-my/strings.xml +++ b/libs/WindowManager/Shell/res/values-my/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"ဘရောင်ဇာတွင် ဖွင့်ရန်"</string> <string name="new_window_text" msgid="6318648868380652280">"ဝင်းဒိုးအသစ်"</string> <string name="manage_windows_text" msgid="5567366688493093920">"ဝင်းဒိုးများ စီမံရန်"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"အချိုးအစား ပြောင်းရန်"</string> <string name="close_text" msgid="4986518933445178928">"ပိတ်ရန်"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"မီနူး ပိတ်ရန်"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"မီနူး ဖွင့်ရန်"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"အက်ပ်တွင်"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"သင်၏ဘရောင်ဇာတွင်"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"အက်ပ်များကို သင့်ဘရောင်ဇာတွင် ဤနေရာ၌ အမြန်ဖွင့်နိုင်သည်"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-nb/strings.xml b/libs/WindowManager/Shell/res/values-nb/strings.xml index 937d6d49c56b..ed6fb900564f 100644 --- a/libs/WindowManager/Shell/res/values-nb/strings.xml +++ b/libs/WindowManager/Shell/res/values-nb/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"Åpne i nettleseren"</string> <string name="new_window_text" msgid="6318648868380652280">"Nytt vindu"</string> <string name="manage_windows_text" msgid="5567366688493093920">"Administrer vinduene"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Endre høyde/bredde-forholdet"</string> <string name="close_text" msgid="4986518933445178928">"Lukk"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Lukk menyen"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Åpne menyen"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"I appen"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"I nettleseren"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Her kan du raskt åpne apper i nettleseren"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-ne/strings.xml b/libs/WindowManager/Shell/res/values-ne/strings.xml index 36e54a5ae187..aff712901ff6 100644 --- a/libs/WindowManager/Shell/res/values-ne/strings.xml +++ b/libs/WindowManager/Shell/res/values-ne/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"ब्राउजरमा खोल्नुहोस्"</string> <string name="new_window_text" msgid="6318648868380652280">"नयाँ विन्डो"</string> <string name="manage_windows_text" msgid="5567366688493093920">"विन्डोहरू व्यवस्थापन गर्नुहोस्"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"एस्पेक्ट रेसियो परिवर्तन गर्नुहोस्"</string> <string name="close_text" msgid="4986518933445178928">"बन्द गर्नुहोस्"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"मेनु बन्द गर्नुहोस्"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"मेनु खोल्नुहोस्"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"एपमा"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"तपाईंको ब्राउजरमा"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"ठिक छ"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"आफ्नो ब्राउजरबाट यहाँ तुरुन्तै एपहरू खोल्नुहोस्"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-or/strings.xml b/libs/WindowManager/Shell/res/values-or/strings.xml index 9d9b773634fc..69540898161c 100644 --- a/libs/WindowManager/Shell/res/values-or/strings.xml +++ b/libs/WindowManager/Shell/res/values-or/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"ବ୍ରାଉଜରରେ ଖୋଲନ୍ତୁ"</string> <string name="new_window_text" msgid="6318648868380652280">"ନୂଆ ୱିଣ୍ଡୋ"</string> <string name="manage_windows_text" msgid="5567366688493093920">"ୱିଣ୍ଡୋଗୁଡ଼ିକୁ ପରିଚାଳନା କରନ୍ତୁ"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"ଚଉଡ଼ା ଓ ଉଚ୍ଚତାର ଅନୁପାତ ପରିବର୍ତ୍ତନ କରନ୍ତୁ"</string> <string name="close_text" msgid="4986518933445178928">"ବନ୍ଦ କରନ୍ତୁ"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"ମେନୁ ବନ୍ଦ କରନ୍ତୁ"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"ମେନୁ ଖୋଲନ୍ତୁ"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"ଆପରେ"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"ଆପଣଙ୍କ ବ୍ରାଉଜରରେ"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"ଠିକ ଅଛି"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"ଏଠାରେ ଆପଣଙ୍କ ବ୍ରାଉଜରରେ ଥିବା ଆପ୍ସକୁ ଶୀଘ୍ର ଖୋଲନ୍ତୁ"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-pa/strings.xml b/libs/WindowManager/Shell/res/values-pa/strings.xml index af13301fb991..c627d7fcc2c5 100644 --- a/libs/WindowManager/Shell/res/values-pa/strings.xml +++ b/libs/WindowManager/Shell/res/values-pa/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"ਬ੍ਰਾਊਜ਼ਰ ਵਿੱਚ ਖੋਲ੍ਹੋ"</string> <string name="new_window_text" msgid="6318648868380652280">"ਨਵੀਂ ਵਿੰਡੋ"</string> <string name="manage_windows_text" msgid="5567366688493093920">"ਵਿੰਡੋਆਂ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰੋ"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"ਆਕਾਰ ਅਨੁਪਾਤ ਬਦਲੋ"</string> <string name="close_text" msgid="4986518933445178928">"ਬੰਦ ਕਰੋ"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"ਮੀਨੂ ਬੰਦ ਕਰੋ"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"ਮੀਨੂ ਖੋਲ੍ਹੋ"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"ਐਪ ਵਿੱਚ"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"ਤੁਹਾਡੇ ਬ੍ਰਾਊਜ਼ਰ ਵਿੱਚ"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"ਠੀਕ ਹੈ"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"ਇੱਥੇ ਆਪਣੇ ਬ੍ਰਾਊਜ਼ਰ ਵਿੱਚ ਤੇਜ਼ੀ ਨਾਲ ਐਪਾਂ ਖੋਲ੍ਹੋ"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-pl/strings.xml b/libs/WindowManager/Shell/res/values-pl/strings.xml index 27080fb77fe1..a138c08d319a 100644 --- a/libs/WindowManager/Shell/res/values-pl/strings.xml +++ b/libs/WindowManager/Shell/res/values-pl/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"Otwórz w przeglądarce"</string> <string name="new_window_text" msgid="6318648868380652280">"Nowe okno"</string> <string name="manage_windows_text" msgid="5567366688493093920">"Zarządzaj oknami"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Zmień format obrazu"</string> <string name="close_text" msgid="4986518933445178928">"Zamknij"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Zamknij menu"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Otwórz menu"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"W aplikacji"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"W przeglądarce"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Tutaj możesz szybko otwierać aplikacje w przeglądarce"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml index 9039cc227b30..9942f6980306 100644 --- a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml +++ b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml @@ -52,10 +52,10 @@ <string name="accessibility_split_right" msgid="8441001008181296837">"Dividir para a direita"</string> <string name="accessibility_split_top" msgid="2789329702027147146">"Dividir para cima"</string> <string name="accessibility_split_bottom" msgid="8694551025220868191">"Dividir para baixo"</string> - <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Como usar o modo uma mão"</string> + <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Como usar o modo para uma mão"</string> <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Para sair, deslize de baixo para cima na tela ou toque em qualquer lugar acima do app"</string> - <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Iniciar o modo uma mão"</string> - <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Sair do modo uma mão"</string> + <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Iniciar o modo para uma mão"</string> + <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Sair do modo para uma mão"</string> <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Configurações de balões do <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Menu flutuante"</string> <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Devolver à pilha"</string> @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"Abrir no navegador"</string> <string name="new_window_text" msgid="6318648868380652280">"Nova janela"</string> <string name="manage_windows_text" msgid="5567366688493093920">"Gerenciar janelas"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Mudar a proporção"</string> <string name="close_text" msgid="4986518933445178928">"Fechar"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Fechar menu"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Abrir o menu"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"No app"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"No navegador"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Abra apps no navegador rapidamente aqui"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-pt/strings.xml b/libs/WindowManager/Shell/res/values-pt/strings.xml index 9039cc227b30..9942f6980306 100644 --- a/libs/WindowManager/Shell/res/values-pt/strings.xml +++ b/libs/WindowManager/Shell/res/values-pt/strings.xml @@ -52,10 +52,10 @@ <string name="accessibility_split_right" msgid="8441001008181296837">"Dividir para a direita"</string> <string name="accessibility_split_top" msgid="2789329702027147146">"Dividir para cima"</string> <string name="accessibility_split_bottom" msgid="8694551025220868191">"Dividir para baixo"</string> - <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Como usar o modo uma mão"</string> + <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Como usar o modo para uma mão"</string> <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Para sair, deslize de baixo para cima na tela ou toque em qualquer lugar acima do app"</string> - <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Iniciar o modo uma mão"</string> - <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Sair do modo uma mão"</string> + <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Iniciar o modo para uma mão"</string> + <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Sair do modo para uma mão"</string> <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Configurações de balões do <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Menu flutuante"</string> <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Devolver à pilha"</string> @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"Abrir no navegador"</string> <string name="new_window_text" msgid="6318648868380652280">"Nova janela"</string> <string name="manage_windows_text" msgid="5567366688493093920">"Gerenciar janelas"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Mudar a proporção"</string> <string name="close_text" msgid="4986518933445178928">"Fechar"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Fechar menu"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Abrir o menu"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"No app"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"No navegador"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Abra apps no navegador rapidamente aqui"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-ro/strings.xml b/libs/WindowManager/Shell/res/values-ro/strings.xml index 52e45bc320fe..df0ee45695b2 100644 --- a/libs/WindowManager/Shell/res/values-ro/strings.xml +++ b/libs/WindowManager/Shell/res/values-ro/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"Deschide în browser"</string> <string name="new_window_text" msgid="6318648868380652280">"Fereastră nouă"</string> <string name="manage_windows_text" msgid="5567366688493093920">"Gestionează ferestrele"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Schimbă raportul de dimensiuni"</string> <string name="close_text" msgid="4986518933445178928">"Închide"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Închide meniul"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Deschide meniul"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"În aplicație"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"În browser"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Deschide rapid aplicații în browser aici"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-ru/strings.xml b/libs/WindowManager/Shell/res/values-ru/strings.xml index 6d32b648acc4..430f1b1448da 100644 --- a/libs/WindowManager/Shell/res/values-ru/strings.xml +++ b/libs/WindowManager/Shell/res/values-ru/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"Открыть в браузере"</string> <string name="new_window_text" msgid="6318648868380652280">"Новое окно"</string> <string name="manage_windows_text" msgid="5567366688493093920">"Управление окнами"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Изменить соотношение сторон"</string> <string name="close_text" msgid="4986518933445178928">"Закрыть"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Закрыть меню"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Открыть меню"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"В приложении"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"В браузере"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"ОК"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Здесь можно быстро открывать приложения в браузере"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-si/strings.xml b/libs/WindowManager/Shell/res/values-si/strings.xml index 7c7f85dee51b..3e3766768a7f 100644 --- a/libs/WindowManager/Shell/res/values-si/strings.xml +++ b/libs/WindowManager/Shell/res/values-si/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"බ්රව්සරයේ විවෘත කරන්න"</string> <string name="new_window_text" msgid="6318648868380652280">"නව කවුළුව"</string> <string name="manage_windows_text" msgid="5567366688493093920">"කවුළු කළමනාකරණය කරන්න"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"දර්ශන අනුපාතය වෙනස් කරන්න"</string> <string name="close_text" msgid="4986518933445178928">"වසන්න"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"මෙනුව වසන්න"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"මෙනුව විවෘත කරන්න"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"යෙදුම තුළ"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"ඔබේ බ්රව්සරය තුළ"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"හරි"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"ඔබේ බ්රව්සරයේ යෙදුම් ඉක්මනින් විවෘත කරන්න"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-sk/strings.xml b/libs/WindowManager/Shell/res/values-sk/strings.xml index c89f6990ee75..56a7edd08b23 100644 --- a/libs/WindowManager/Shell/res/values-sk/strings.xml +++ b/libs/WindowManager/Shell/res/values-sk/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"Otvoriť v prehliadači"</string> <string name="new_window_text" msgid="6318648868380652280">"Nové okno"</string> <string name="manage_windows_text" msgid="5567366688493093920">"Správa okien"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Zmeniť pomer strán"</string> <string name="close_text" msgid="4986518933445178928">"Zavrieť"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Zavrieť ponuku"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Otvoriť ponuku"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"V aplikácii"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"V prehliadači"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Tu môžete rýchlo otvárať aplikácie v prehliadači"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-sl/strings.xml b/libs/WindowManager/Shell/res/values-sl/strings.xml index b064ce5038c1..b6344c981fc9 100644 --- a/libs/WindowManager/Shell/res/values-sl/strings.xml +++ b/libs/WindowManager/Shell/res/values-sl/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"Odpri v brskalniku"</string> <string name="new_window_text" msgid="6318648868380652280">"Novo okno"</string> <string name="manage_windows_text" msgid="5567366688493093920">"Upravljanje oken"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Sprememba razmerja stranic"</string> <string name="close_text" msgid="4986518933445178928">"Zapri"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Zapri meni"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Odpri meni"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"V aplikaciji"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"V brskalniku"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"V redu"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Tukaj hitro odprete aplikacije v brskalniku"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-sq/strings.xml b/libs/WindowManager/Shell/res/values-sq/strings.xml index b1de743f7b61..ab7499d505f8 100644 --- a/libs/WindowManager/Shell/res/values-sq/strings.xml +++ b/libs/WindowManager/Shell/res/values-sq/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"Hape në shfletues"</string> <string name="new_window_text" msgid="6318648868380652280">"Dritare e re"</string> <string name="manage_windows_text" msgid="5567366688493093920">"Menaxho dritaret"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Ndrysho raportin e pamjes"</string> <string name="close_text" msgid="4986518933445178928">"Mbyll"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Mbyll menynë"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Hap menynë"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Në aplikacion"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Në shfletuesin tënd"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Në rregull"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Hapi me shpejtësi aplikacionet në shfletues këtu"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-sr/strings.xml b/libs/WindowManager/Shell/res/values-sr/strings.xml index 50faf80190ea..773ed16dc4b9 100644 --- a/libs/WindowManager/Shell/res/values-sr/strings.xml +++ b/libs/WindowManager/Shell/res/values-sr/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"Отворите у прегледачу"</string> <string name="new_window_text" msgid="6318648868380652280">"Нови прозор"</string> <string name="manage_windows_text" msgid="5567366688493093920">"Управљајте прозорима"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Промените размеру"</string> <string name="close_text" msgid="4986518933445178928">"Затворите"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Затворите мени"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Отворите мени"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"У апликацији"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"У прегледачу"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Потврди"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Овде можете брзо да отварате апликације у прегледачу"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-sv/strings.xml b/libs/WindowManager/Shell/res/values-sv/strings.xml index 51ef239ec2e9..6f6a97b4495f 100644 --- a/libs/WindowManager/Shell/res/values-sv/strings.xml +++ b/libs/WindowManager/Shell/res/values-sv/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"Öppna i webbläsaren"</string> <string name="new_window_text" msgid="6318648868380652280">"Nytt fönster"</string> <string name="manage_windows_text" msgid="5567366688493093920">"Hantera fönster"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Ändra bildformat"</string> <string name="close_text" msgid="4986518933445178928">"Stäng"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Stäng menyn"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Öppna menyn"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"I appen"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"I webbläsaren"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Öppna snabbt appar i webbläsaren här"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-sw/strings.xml b/libs/WindowManager/Shell/res/values-sw/strings.xml index ff5d423170c2..72b7384fa83a 100644 --- a/libs/WindowManager/Shell/res/values-sw/strings.xml +++ b/libs/WindowManager/Shell/res/values-sw/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"Fungua katika kivinjari"</string> <string name="new_window_text" msgid="6318648868380652280">"Dirisha Jipya"</string> <string name="manage_windows_text" msgid="5567366688493093920">"Dhibiti Windows"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Badilisha uwiano"</string> <string name="close_text" msgid="4986518933445178928">"Funga"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Funga Menyu"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Fungua Menyu"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Kwenye programu"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Kwenye kivinjari chako"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Sawa"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Fungua programu kwa haraka katika kivinjari chako hapa"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-ta/strings.xml b/libs/WindowManager/Shell/res/values-ta/strings.xml index 953c64d67106..9d902912b377 100644 --- a/libs/WindowManager/Shell/res/values-ta/strings.xml +++ b/libs/WindowManager/Shell/res/values-ta/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"உலாவியில் திறக்கும்"</string> <string name="new_window_text" msgid="6318648868380652280">"புதிய சாளரம்"</string> <string name="manage_windows_text" msgid="5567366688493093920">"சாளரங்களை நிர்வகிக்கலாம்"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"தோற்ற விகிதத்தை மாற்று"</string> <string name="close_text" msgid="4986518933445178928">"மூடும்"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"மெனுவை மூடும்"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"மெனுவைத் திறக்கும்"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"ஆப்ஸில்"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"உங்கள் பிரவுசரில்"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"சரி"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"உங்கள் உலாவியில் ஆப்ஸை இங்கே விரைவாகத் திறக்கலாம்"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-te/strings.xml b/libs/WindowManager/Shell/res/values-te/strings.xml index 2efb0ba55970..3c7c06a4fc1a 100644 --- a/libs/WindowManager/Shell/res/values-te/strings.xml +++ b/libs/WindowManager/Shell/res/values-te/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"బ్రౌజర్లో తెరవండి"</string> <string name="new_window_text" msgid="6318648868380652280">"కొత్త విండో"</string> <string name="manage_windows_text" msgid="5567366688493093920">"విండోలను మేనేజ్ చేయండి"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"ఆకార నిష్పత్తిని మార్చండి"</string> <string name="close_text" msgid="4986518933445178928">"మూసివేయండి"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"మెనూను మూసివేయండి"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"మెనూను తెరవండి"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"యాప్లో"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"మీ బ్రౌజర్లో"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"సరే"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"మీ బ్రౌజర్లో ఇక్కడ యాప్లను వేగంగా తెరవండి"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-th/strings.xml b/libs/WindowManager/Shell/res/values-th/strings.xml index 3d775d2ee8af..9071bfb66e92 100644 --- a/libs/WindowManager/Shell/res/values-th/strings.xml +++ b/libs/WindowManager/Shell/res/values-th/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"เปิดในเบราว์เซอร์"</string> <string name="new_window_text" msgid="6318648868380652280">"หน้าต่างใหม่"</string> <string name="manage_windows_text" msgid="5567366688493093920">"จัดการหน้าต่าง"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"เปลี่ยนสัดส่วนการแสดงผล"</string> <string name="close_text" msgid="4986518933445178928">"ปิด"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"ปิดเมนู"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"เปิดเมนู"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"ในแอป"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"ในเบราว์เซอร์"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"ตกลง"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"เปิดแอปในเบราว์เซอร์ได้อย่างรวดเร็วที่นี่"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-tl/strings.xml b/libs/WindowManager/Shell/res/values-tl/strings.xml index a57cb8b087a0..a00f7839186b 100644 --- a/libs/WindowManager/Shell/res/values-tl/strings.xml +++ b/libs/WindowManager/Shell/res/values-tl/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"Buksan sa browser"</string> <string name="new_window_text" msgid="6318648868380652280">"Bagong Window"</string> <string name="manage_windows_text" msgid="5567366688493093920">"Pamahalaan ang Mga Window"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Baguhin ang aspect ratio"</string> <string name="close_text" msgid="4986518933445178928">"Isara"</string> <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> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Sa app"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Sa iyong browser"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Mabilis na buksan ang mga app sa iyong browser dito"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-tr/strings.xml b/libs/WindowManager/Shell/res/values-tr/strings.xml index bea4a3513c0f..8310a66e9d33 100644 --- a/libs/WindowManager/Shell/res/values-tr/strings.xml +++ b/libs/WindowManager/Shell/res/values-tr/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"Tarayıcıda aç"</string> <string name="new_window_text" msgid="6318648868380652280">"Yeni Pencere"</string> <string name="manage_windows_text" msgid="5567366688493093920">"Pencereleri yönet"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"En boy oranını değiştir"</string> <string name="close_text" msgid="4986518933445178928">"Kapat"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Menüyü kapat"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Menüyü aç"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Uygulamada"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Tarayıcınızda"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"Tamam"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Buradan tarayıcınızda uygulamaları hızlıca açın"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-uk/strings.xml b/libs/WindowManager/Shell/res/values-uk/strings.xml index 91f665e04178..624a19e67724 100644 --- a/libs/WindowManager/Shell/res/values-uk/strings.xml +++ b/libs/WindowManager/Shell/res/values-uk/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"Відкрити у вебпереглядачі"</string> <string name="new_window_text" msgid="6318648868380652280">"Нове вікно"</string> <string name="manage_windows_text" msgid="5567366688493093920">"Керувати вікнами"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Змінити формат"</string> <string name="close_text" msgid="4986518933445178928">"Закрити"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Закрити меню"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Відкрити меню"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"У додатку"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"У вебпереглядачі"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Швидко відкривайте додатки у вебпереглядачі"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-ur/strings.xml b/libs/WindowManager/Shell/res/values-ur/strings.xml index 6125bfde56ed..2ccaf50fee21 100644 --- a/libs/WindowManager/Shell/res/values-ur/strings.xml +++ b/libs/WindowManager/Shell/res/values-ur/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"براؤزر میں کھولیں"</string> <string name="new_window_text" msgid="6318648868380652280">"نئی ونڈو"</string> <string name="manage_windows_text" msgid="5567366688493093920">"Windows کا نظم کریں"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"تناسبی شرح کو تبدیل کریں"</string> <string name="close_text" msgid="4986518933445178928">"بند کریں"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"مینیو بند کریں"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"مینو کھولیں"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"ایپ میں"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"آپ کے براؤزر میں"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"ٹھیک ہے"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"اپنے براؤزر میں ایپس کو فوری طور پر یہاں کھولیں"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-uz/strings.xml b/libs/WindowManager/Shell/res/values-uz/strings.xml index 63e818ca0512..88edc929528b 100644 --- a/libs/WindowManager/Shell/res/values-uz/strings.xml +++ b/libs/WindowManager/Shell/res/values-uz/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"Brauzerda ochish"</string> <string name="new_window_text" msgid="6318648868380652280">"Yangi oyna"</string> <string name="manage_windows_text" msgid="5567366688493093920">"Oynalarni boshqarish"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Tomonlar nisbatini oʻzgartirish"</string> <string name="close_text" msgid="4986518933445178928">"Yopish"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Menyuni yopish"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Menyuni ochish"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Ilovada"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Brauzerda"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Brauzerda ilovalarni shu yerda tezkor oching"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-vi/strings.xml b/libs/WindowManager/Shell/res/values-vi/strings.xml index 7114f1d36b7d..c1c7653ce90c 100644 --- a/libs/WindowManager/Shell/res/values-vi/strings.xml +++ b/libs/WindowManager/Shell/res/values-vi/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"Mở trong trình duyệt"</string> <string name="new_window_text" msgid="6318648868380652280">"Cửa sổ mới"</string> <string name="manage_windows_text" msgid="5567366688493093920">"Quản lý cửa sổ"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Thay đổi tỷ lệ khung hình"</string> <string name="close_text" msgid="4986518933445178928">"Đóng"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Đóng trình đơn"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Mở Trình đơn"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Trong ứng dụng"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Trên trình duyệt"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"OK"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Mở nhanh các ứng dụng trong trình duyệt tại đây"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml index 15c45d9f678c..83e15d8cbdf6 100644 --- a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml +++ b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"在浏览器中打开"</string> <string name="new_window_text" msgid="6318648868380652280">"新窗口"</string> <string name="manage_windows_text" msgid="5567366688493093920">"管理窗口"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"更改宽高比"</string> <string name="close_text" msgid="4986518933445178928">"关闭"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"关闭菜单"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"打开菜单"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"在此应用内"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"在浏览器中"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"确定"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"在此处快速在浏览器中打开应用"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml index f7f4a2ad08b5..f60b3efc6f38 100644 --- a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml +++ b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"在瀏覽器中開啟"</string> <string name="new_window_text" msgid="6318648868380652280">"新視窗"</string> <string name="manage_windows_text" msgid="5567366688493093920">"管理視窗"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"變更長寬比"</string> <string name="close_text" msgid="4986518933445178928">"關閉"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"關閉選單"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"打開選單"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"在應用程式內"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"在瀏覽器中"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"確定"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"在此透過瀏覽器快速開啟應用程式"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml index 000944b210b0..b2227deeccc3 100644 --- a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml +++ b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"在瀏覽器中開啟"</string> <string name="new_window_text" msgid="6318648868380652280">"新視窗"</string> <string name="manage_windows_text" msgid="5567366688493093920">"管理視窗"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"變更顯示比例"</string> <string name="close_text" msgid="4986518933445178928">"關閉"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"關閉選單"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"開啟選單"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"使用應用程式"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"使用瀏覽器"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"確定"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"在這個瀏覽器中快速開啟應用程式"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values-zu/strings.xml b/libs/WindowManager/Shell/res/values-zu/strings.xml index 63eeb5f1d3d7..10d904fa17d2 100644 --- a/libs/WindowManager/Shell/res/values-zu/strings.xml +++ b/libs/WindowManager/Shell/res/values-zu/strings.xml @@ -127,8 +127,7 @@ <string name="open_in_browser_text" msgid="9181692926376072904">"Vula kubhrawuza"</string> <string name="new_window_text" msgid="6318648868380652280">"Iwindi Elisha"</string> <string name="manage_windows_text" msgid="5567366688493093920">"Phatha Amawindi"</string> - <!-- no translation found for change_aspect_ratio_text (9104456064548212806) --> - <skip /> + <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Shintsha ukubukeka kwesilinganiselo"</string> <string name="close_text" msgid="4986518933445178928">"Vala"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Vala Imenyu"</string> <string name="desktop_mode_app_header_chip_text" msgid="6366422614991687237">"Vula Imenyu"</string> @@ -146,6 +145,5 @@ <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"Ku-app"</string> <string name="open_by_default_dialog_in_browser_text" msgid="8042769465958497081">"Kubhrawuza yakho"</string> <string name="open_by_default_dialog_dismiss_button_text" msgid="3487238795534582291">"KULUNGILE"</string> - <!-- no translation found for desktop_windowing_app_to_web_education_text (1599668769538703570) --> - <skip /> + <string name="desktop_windowing_app_to_web_education_text" msgid="1599668769538703570">"Ngokushesha vula ama-app ebhrawuzeni yakho lapha"</string> </resources> diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml index 8f1ef6c7e49e..012579a6d40c 100644 --- a/libs/WindowManager/Shell/res/values/strings.xml +++ b/libs/WindowManager/Shell/res/values/strings.xml @@ -301,6 +301,8 @@ <string name="screenshot_text">Screenshot</string> <!-- Accessibility text for the handle menu open in browser button [CHAR LIMIT=NONE] --> <string name="open_in_browser_text">Open in browser</string> + <!-- Accessibility text for the handle menu open in app button [CHAR LIMIT=NONE] --> + <string name="open_in_app_text">Open in App</string> <!-- Accessibility text for the handle menu new window button [CHAR LIMIT=NONE] --> <string name="new_window_text">New Window</string> <!-- Accessibility text for the handle menu new window button [CHAR LIMIT=NONE] --> diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleBarLocation.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleBarLocation.kt index 191875d38daf..84a22b873aaf 100644 --- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleBarLocation.kt +++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleBarLocation.kt @@ -15,6 +15,7 @@ */ package com.android.wm.shell.shared.bubbles +import android.annotation.IntDef import android.os.Parcel import android.os.Parcelable @@ -60,4 +61,36 @@ enum class BubbleBarLocation : Parcelable { override fun newArray(size: Int) = arrayOfNulls<BubbleBarLocation>(size) } } + + /** Define set of constants that allow to determine why location changed. */ + @IntDef( + UpdateSource.DRAG_BAR, + UpdateSource.DRAG_BUBBLE, + UpdateSource.DRAG_EXP_VIEW, + UpdateSource.A11Y_ACTION_BAR, + UpdateSource.A11Y_ACTION_BUBBLE, + UpdateSource.A11Y_ACTION_EXP_VIEW, + ) + @Retention(AnnotationRetention.SOURCE) + annotation class UpdateSource { + companion object { + /** Location changed from dragging the bar */ + const val DRAG_BAR = 1 + + /** Location changed from dragging the bubble */ + const val DRAG_BUBBLE = 2 + + /** Location changed from dragging the expanded view */ + const val DRAG_EXP_VIEW = 3 + + /** Location changed via a11y action on the bar */ + const val A11Y_ACTION_BAR = 4 + + /** Location changed via a11y action on the bubble */ + const val A11Y_ACTION_BUBBLE = 5 + + /** Location changed via a11y action on the expanded view */ + const val A11Y_ACTION_EXP_VIEW = 6 + } + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/AppToWebUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/AppToWebUtils.kt index 65132fe89063..7243ea36b137 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/AppToWebUtils.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/AppToWebUtils.kt @@ -20,7 +20,9 @@ package com.android.wm.shell.apptoweb import android.content.Context import android.content.Intent +import android.content.Intent.ACTION_VIEW import android.content.Intent.FLAG_ACTIVITY_NEW_TASK +import android.content.Intent.FLAG_ACTIVITY_REQUIRE_NON_BROWSER import android.content.pm.PackageManager import android.content.pm.verify.domain.DomainVerificationManager import android.content.pm.verify.domain.DomainVerificationUserState @@ -31,7 +33,7 @@ import com.android.wm.shell.protolog.ShellProtoLogGroup private const val TAG = "AppToWebUtils" private val GenericBrowserIntent = Intent() - .setAction(Intent.ACTION_VIEW) + .setAction(ACTION_VIEW) .addCategory(Intent.CATEGORY_BROWSABLE) .setData(Uri.parse("http:")) @@ -67,6 +69,20 @@ fun getBrowserIntent(uri: Uri, packageManager: PackageManager): Intent? { } /** + * Returns intent if there is a non-browser application available to handle the uri. Otherwise, + * returns null. + */ +fun getAppIntent(uri: Uri, packageManager: PackageManager): Intent? { + val intent = Intent(ACTION_VIEW, uri).apply { + flags = FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_REQUIRE_NON_BROWSER + } + // If there is no application available to handle intent, return null + val component = intent.resolveActivity(packageManager) ?: return null + intent.setComponent(component) + return intent +} + +/** * Returns the [DomainVerificationUserState] of the user associated with the given * [DomainVerificationManager] and the given package. */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java index c92a2786e49b..ce7a97703f44 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java @@ -1122,7 +1122,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont final BackMotionEvent backFinish = mCurrentTracker .createProgressEvent(); dispatchOnBackProgressed(mActiveCallback, backFinish); - if (!mBackGestureStarted) { + if (mCurrentTracker.isFinished()) { // if the down -> up gesture happened before animation // start, we have to trigger the uninterruptible transition // to finish the back animation. 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 14f8cc74bfc5..39dc26797a81 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 @@ -740,8 +740,10 @@ public class BubbleController implements ConfigurationChangeListener, /** * Update bubble bar location and trigger and update to listeners */ - public void setBubbleBarLocation(BubbleBarLocation bubbleBarLocation) { + public void setBubbleBarLocation(BubbleBarLocation bubbleBarLocation, + @BubbleBarLocation.UpdateSource int source) { if (canShowAsBubbleBar()) { + BubbleBarLocation previousLocation = mBubblePositioner.getBubbleBarLocation(); mBubblePositioner.setBubbleBarLocation(bubbleBarLocation); if (mLayerView != null && !mLayerView.isExpandedViewDragged()) { mLayerView.updateExpandedView(); @@ -749,13 +751,47 @@ public class BubbleController implements ConfigurationChangeListener, BubbleBarUpdate bubbleBarUpdate = new BubbleBarUpdate(); bubbleBarUpdate.bubbleBarLocation = bubbleBarLocation; mBubbleStateListener.onBubbleStateChange(bubbleBarUpdate); + + logBubbleBarLocationIfChanged(bubbleBarLocation, previousLocation, source); + } + } + + private void logBubbleBarLocationIfChanged(BubbleBarLocation location, + BubbleBarLocation previous, + @BubbleBarLocation.UpdateSource int source) { + if (mLayerView == null) { + return; + } + boolean isRtl = mLayerView.isLayoutRtl(); + boolean wasLeft = previous.isOnLeft(isRtl); + boolean onLeft = location.isOnLeft(isRtl); + if (wasLeft == onLeft) { + // No changes, skip logging + return; + } + switch (source) { + case BubbleBarLocation.UpdateSource.DRAG_BAR: + case BubbleBarLocation.UpdateSource.A11Y_ACTION_BAR: + mLogger.log(onLeft ? BubbleLogger.Event.BUBBLE_BAR_MOVED_LEFT_DRAG_BAR + : BubbleLogger.Event.BUBBLE_BAR_MOVED_RIGHT_DRAG_BAR); + break; + case BubbleBarLocation.UpdateSource.DRAG_BUBBLE: + case BubbleBarLocation.UpdateSource.A11Y_ACTION_BUBBLE: + mLogger.log(onLeft ? BubbleLogger.Event.BUBBLE_BAR_MOVED_LEFT_DRAG_BUBBLE + : BubbleLogger.Event.BUBBLE_BAR_MOVED_RIGHT_DRAG_BUBBLE); + break; + case BubbleBarLocation.UpdateSource.DRAG_EXP_VIEW: + case BubbleBarLocation.UpdateSource.A11Y_ACTION_EXP_VIEW: + // TODO(b/349845968): move logging from BubbleBarLayerView to here + break; } } /** * Animate bubble bar to the given location. The location change is transient. It does not * update the state of the bubble bar. - * To update bubble bar pinned location, use {@link #setBubbleBarLocation(BubbleBarLocation)}. + * To update bubble bar pinned location, use + * {@link #setBubbleBarLocation(BubbleBarLocation, int)}. */ public void animateBubbleBarLocation(BubbleBarLocation bubbleBarLocation) { if (canShowAsBubbleBar()) { @@ -1259,6 +1295,14 @@ public class BubbleController implements ConfigurationChangeListener, // We still have bubbles, if we dragged an individual bubble to dismiss we were expanded // so re-expand to whatever is selected. showExpandedViewForBubbleBar(); + if (bubbleKey.equals(selectedBubbleKey)) { + // We dragged the selected bubble to dismiss, log switch event + if (mBubbleData.getSelectedBubble() instanceof Bubble) { + // Log only bubbles as overflow can't be dragged + mLogger.log((Bubble) mBubbleData.getSelectedBubble(), + BubbleLogger.Event.BUBBLE_BAR_BUBBLE_SWITCHED); + } + } } } @@ -1301,10 +1345,16 @@ public class BubbleController implements ConfigurationChangeListener, public void expandStackAndSelectBubbleFromLauncher(String key, int topOnScreen) { mBubblePositioner.setBubbleBarTopOnScreen(topOnScreen); + boolean wasExpanded = (mLayerView != null && mLayerView.isExpanded()); + if (BubbleOverflow.KEY.equals(key)) { mBubbleData.setSelectedBubbleFromLauncher(mBubbleData.getOverflow()); mLayerView.showExpandedView(mBubbleData.getOverflow()); - mLogger.log(BubbleLogger.Event.BUBBLE_BAR_EXPANDED); + if (wasExpanded) { + mLogger.log(BubbleLogger.Event.BUBBLE_BAR_BUBBLE_SWITCHED); + } else { + mLogger.log(BubbleLogger.Event.BUBBLE_BAR_EXPANDED); + } return; } @@ -1316,7 +1366,11 @@ public class BubbleController implements ConfigurationChangeListener, // already in the stack mBubbleData.setSelectedBubbleFromLauncher(b); mLayerView.showExpandedView(b); - mLogger.log(b, BubbleLogger.Event.BUBBLE_BAR_EXPANDED); + if (wasExpanded) { + mLogger.log(b, BubbleLogger.Event.BUBBLE_BAR_BUBBLE_SWITCHED); + } else { + mLogger.log(b, BubbleLogger.Event.BUBBLE_BAR_EXPANDED); + } } else if (mBubbleData.hasOverflowBubbleWithKey(b.getKey())) { // TODO: (b/271468319) handle overflow } else { @@ -2045,6 +2099,9 @@ public class BubbleController implements ConfigurationChangeListener, // Only need to update the layer view if we're currently expanded for selection changes. if (mLayerView != null && mLayerView.isExpanded()) { mLayerView.showExpandedView(selectedBubble); + if (selectedBubble instanceof Bubble bubble) { + mLogger.log(bubble, BubbleLogger.Event.BUBBLE_BAR_BUBBLE_SWITCHED); + } } } }; @@ -2568,9 +2625,10 @@ public class BubbleController implements ConfigurationChangeListener, } @Override - public void setBubbleBarLocation(BubbleBarLocation location) { + public void setBubbleBarLocation(BubbleBarLocation location, + @BubbleBarLocation.UpdateSource int source) { mMainExecutor.execute(() -> - mController.setBubbleBarLocation(location)); + mController.setBubbleBarLocation(location, source)); } @Override diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedViewManager.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedViewManager.kt index ec4854b47aff..6423eed59165 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedViewManager.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedViewManager.kt @@ -32,7 +32,10 @@ interface BubbleExpandedViewManager { fun isStackExpanded(): Boolean fun isShowingAsBubbleBar(): Boolean fun hideCurrentInputMethod() - fun updateBubbleBarLocation(location: BubbleBarLocation) + fun updateBubbleBarLocation( + location: BubbleBarLocation, + @BubbleBarLocation.UpdateSource source: Int, + ) companion object { /** @@ -82,8 +85,11 @@ interface BubbleExpandedViewManager { controller.hideCurrentInputMethod() } - override fun updateBubbleBarLocation(location: BubbleBarLocation) { - controller.bubbleBarLocation = location + override fun updateBubbleBarLocation( + location: BubbleBarLocation, + @BubbleBarLocation.UpdateSource source: Int, + ) { + controller.setBubbleBarLocation(location, source) } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl index 1855b938f48e..9c2d35431554 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl @@ -44,7 +44,7 @@ interface IBubbles { oneway void showUserEducation(in int positionX, in int positionY) = 8; - oneway void setBubbleBarLocation(in BubbleBarLocation location) = 9; + oneway void setBubbleBarLocation(in BubbleBarLocation location, in int source) = 9; oneway void updateBubbleBarTopOnScreen(in int topOnScreen) = 10; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java index 2a50e4d0d74b..3764bcd42ac6 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java @@ -222,7 +222,7 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView mHandleView.setAccessibilityDelegate(new HandleViewAccessibilityDelegate()); } - mMenuViewController = new BubbleBarMenuViewController(mContext, this); + mMenuViewController = new BubbleBarMenuViewController(mContext, mHandleView, this); mMenuViewController.setListener(new BubbleBarMenuViewController.Listener() { @Override public void onMenuVisibilityChanged(boolean visible) { @@ -637,11 +637,13 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView return true; } if (action == R.id.action_move_bubble_bar_left) { - mManager.updateBubbleBarLocation(BubbleBarLocation.LEFT); + mManager.updateBubbleBarLocation(BubbleBarLocation.LEFT, + BubbleBarLocation.UpdateSource.A11Y_ACTION_EXP_VIEW); return true; } if (action == R.id.action_move_bubble_bar_right) { - mManager.updateBubbleBarLocation(BubbleBarLocation.RIGHT); + mManager.updateBubbleBarLocation(BubbleBarLocation.RIGHT, + BubbleBarLocation.UpdateSource.A11Y_ACTION_EXP_VIEW); return true; } return false; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleView.java index e781c07f01a7..712e41b0b3c5 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleView.java @@ -17,17 +17,18 @@ package com.android.wm.shell.bubbles.bar; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; +import android.animation.ArgbEvaluator; import android.animation.ObjectAnimator; import android.annotation.Nullable; import android.content.Context; -import android.graphics.Outline; -import android.graphics.Path; -import android.graphics.RectF; +import android.graphics.Canvas; +import android.graphics.Paint; import android.util.AttributeSet; import android.view.View; -import android.view.ViewOutlineProvider; import androidx.annotation.ColorInt; +import androidx.annotation.VisibleForTesting; +import androidx.core.animation.IntProperty; import androidx.core.content.ContextCompat; import com.android.wm.shell.R; @@ -37,14 +38,33 @@ import com.android.wm.shell.R; */ public class BubbleBarHandleView extends View { private static final long COLOR_CHANGE_DURATION = 120; - // Path used to draw the dots - private final Path mPath = new Path(); + /** Custom property to set handle color. */ + private static final IntProperty<BubbleBarHandleView> HANDLE_COLOR = new IntProperty<>( + "handleColor") { + @Override + public void setValue(BubbleBarHandleView bubbleBarHandleView, int color) { + bubbleBarHandleView.setHandleColor(color); + } + + @Override + public Integer get(BubbleBarHandleView bubbleBarHandleView) { + return bubbleBarHandleView.getHandleColor(); + } + }; + + @VisibleForTesting + final Paint mHandlePaint = new Paint(); private final @ColorInt int mHandleLightColor; private final @ColorInt int mHandleDarkColor; - private @ColorInt int mCurrentColor; + private final ArgbEvaluator mArgbEvaluator = ArgbEvaluator.getInstance(); + private final float mHandleHeight; + private final float mHandleWidth; + private float mCurrentHandleHeight; + private float mCurrentHandleWidth; @Nullable private ObjectAnimator mColorChangeAnim; + private @ColorInt int mRegionSamplerColor; public BubbleBarHandleView(Context context) { this(context, null /* attrs */); @@ -61,30 +81,52 @@ public class BubbleBarHandleView extends View { public BubbleBarHandleView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); - final int handleHeight = getResources().getDimensionPixelSize( + mHandlePaint.setFlags(Paint.ANTI_ALIAS_FLAG); + mHandlePaint.setStyle(Paint.Style.FILL); + mHandlePaint.setColor(0); + mHandleHeight = getResources().getDimensionPixelSize( R.dimen.bubble_bar_expanded_view_handle_height); + mHandleWidth = getResources().getDimensionPixelSize( + R.dimen.bubble_bar_expanded_view_caption_width); mHandleLightColor = ContextCompat.getColor(getContext(), R.color.bubble_bar_expanded_view_handle_light); mHandleDarkColor = ContextCompat.getColor(getContext(), R.color.bubble_bar_expanded_view_handle_dark); - - setClipToOutline(true); - setOutlineProvider(new ViewOutlineProvider() { - @Override - public void getOutline(View view, Outline outline) { - final int handleCenterY = view.getHeight() / 2; - final int handleTop = handleCenterY - handleHeight / 2; - final int handleBottom = handleTop + handleHeight; - final int radius = handleHeight / 2; - RectF handle = new RectF(/* left = */ 0, handleTop, view.getWidth(), handleBottom); - mPath.reset(); - mPath.addRoundRect(handle, radius, radius, Path.Direction.CW); - outline.setPath(mPath); - } - }); + mCurrentHandleHeight = mHandleHeight; + mCurrentHandleWidth = mHandleWidth; setContentDescription(getResources().getString(R.string.handle_text)); } + private void setHandleColor(int color) { + mHandlePaint.setColor(color); + invalidate(); + } + + private int getHandleColor() { + return mHandlePaint.getColor(); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + float handleLeft = (getWidth() - mCurrentHandleWidth) / 2; + float handleRight = handleLeft + mCurrentHandleWidth; + float handleCenterY = (float) getHeight() / 2; + float handleTop = (int) (handleCenterY - mCurrentHandleHeight / 2); + float handleBottom = handleTop + mCurrentHandleHeight; + float cornerRadius = mCurrentHandleHeight / 2; + canvas.drawRoundRect(handleLeft, handleTop, handleRight, handleBottom, cornerRadius, + cornerRadius, mHandlePaint); + } + + /** Sets handle width, height and color. Does not change the layout properties */ + private void setHandleProperties(float width, float height, int color) { + mCurrentHandleHeight = height; + mCurrentHandleWidth = width; + mHandlePaint.setColor(color); + invalidate(); + } + /** * Updates the handle color. * @@ -94,15 +136,15 @@ public class BubbleBarHandleView extends View { */ public void updateHandleColor(boolean isRegionDark, boolean animated) { int newColor = isRegionDark ? mHandleLightColor : mHandleDarkColor; - if (newColor == mCurrentColor) { + if (newColor == mRegionSamplerColor) { return; } + mRegionSamplerColor = newColor; if (mColorChangeAnim != null) { mColorChangeAnim.cancel(); } - mCurrentColor = newColor; if (animated) { - mColorChangeAnim = ObjectAnimator.ofArgb(this, "backgroundColor", newColor); + mColorChangeAnim = ObjectAnimator.ofArgb(this, HANDLE_COLOR, newColor); mColorChangeAnim.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { @@ -112,7 +154,39 @@ public class BubbleBarHandleView extends View { mColorChangeAnim.setDuration(COLOR_CHANGE_DURATION); mColorChangeAnim.start(); } else { - setBackgroundColor(newColor); + setHandleColor(newColor); } } + + /** Returns handle padding top. */ + public int getHandlePaddingTop() { + return (getHeight() - getResources().getDimensionPixelSize( + R.dimen.bubble_bar_expanded_view_handle_height)) / 2; + } + + /** Animates handle for the bubble menu. */ + public void animateHandleForMenu(float progress, float widthDelta, float heightDelta, + int menuColor) { + float currentWidth = mHandleWidth + widthDelta * progress; + float currentHeight = mHandleHeight + heightDelta * progress; + int color = (int) mArgbEvaluator.evaluate(progress, mRegionSamplerColor, menuColor); + setHandleProperties(currentWidth, currentHeight, color); + setTranslationY(heightDelta * progress / 2); + } + + /** Restores all the properties that were animated to the default values. */ + public void restoreAnimationDefaults() { + setHandleProperties(mHandleWidth, mHandleHeight, mRegionSamplerColor); + setTranslationY(0); + } + + /** Returns the handle height. */ + public int getHandleHeight() { + return (int) mHandleHeight; + } + + /** Returns the handle width. */ + public int getHandleWidth() { + return (int) mHandleWidth; + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java index 999ce17905ef..0c05e3c5115c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java @@ -19,6 +19,7 @@ package com.android.wm.shell.bubbles.bar; import static com.android.wm.shell.shared.animation.Interpolators.ALPHA_IN; import static com.android.wm.shell.shared.animation.Interpolators.ALPHA_OUT; import static com.android.wm.shell.bubbles.Bubbles.DISMISS_USER_GESTURE; +import static com.android.wm.shell.shared.bubbles.BubbleConstants.BUBBLE_EXPANDED_SCRIM_ALPHA; import android.annotation.Nullable; import android.content.Context; @@ -66,8 +67,6 @@ public class BubbleBarLayerView extends FrameLayout private static final String TAG = BubbleBarLayerView.class.getSimpleName(); - private static final float SCRIM_ALPHA = 0.2f; - private final BubbleController mBubbleController; private final BubbleData mBubbleData; private final BubblePositioner mPositioner; @@ -386,7 +385,7 @@ public class BubbleBarLayerView extends FrameLayout if (show) { mScrimView.animate() .setInterpolator(ALPHA_IN) - .alpha(SCRIM_ALPHA) + .alpha(BUBBLE_EXPANDED_SCRIM_ALPHA) .start(); } else { mScrimView.animate() @@ -442,7 +441,8 @@ public class BubbleBarLayerView extends FrameLayout @Override public void onRelease(@NonNull BubbleBarLocation location) { - mBubbleController.setBubbleBarLocation(location); + mBubbleController.setBubbleBarLocation(location, + BubbleBarLocation.UpdateSource.DRAG_EXP_VIEW); if (location != mInitialLocation) { BubbleLogger.Event event = location.isOnLeft(isLayoutRtl()) ? BubbleLogger.Event.BUBBLE_BAR_MOVED_LEFT_DRAG_EXP_VIEW diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuView.java index 0ee20ef1731f..99e20097e61c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuView.java @@ -47,6 +47,10 @@ public class BubbleBarMenuView extends LinearLayout { private ImageView mBubbleIconView; private ImageView mBubbleDismissIconView; private TextView mBubbleTitleView; + // The animation has three stages. Each stage transition lasts until the animation ends. In + // stage 1, the title item content fades in. In stage 2, the background of the option items + // fades in. In stage 3, the option item content fades in. + private static final int SHOW_MENU_STAGES_COUNT = 3; public BubbleBarMenuView(Context context) { this(context, null /* attrs */); @@ -97,6 +101,35 @@ public class BubbleBarMenuView extends LinearLayout { } } + /** Animates the menu from the specified start scale. */ + public void animateFromStartScale(float currentScale, float progress) { + int menuItemElevation = getResources().getDimensionPixelSize( + R.dimen.bubble_manage_menu_elevation); + setScaleX(currentScale); + setScaleY(currentScale); + setAlphaForTitleViews(progress); + mBubbleSectionView.setElevation(menuItemElevation * progress); + float actionsBackgroundAlpha = Math.max(0, + (progress - (float) 1 / SHOW_MENU_STAGES_COUNT) * (SHOW_MENU_STAGES_COUNT - 1)); + float actionItemsAlpha = Math.max(0, + (progress - (float) 2 / SHOW_MENU_STAGES_COUNT) * SHOW_MENU_STAGES_COUNT); + mActionsSectionView.setAlpha(actionsBackgroundAlpha); + mActionsSectionView.setElevation(menuItemElevation * actionsBackgroundAlpha); + setMenuItemViewsAlpha(actionItemsAlpha); + } + + private void setAlphaForTitleViews(float alpha) { + mBubbleIconView.setAlpha(alpha); + mBubbleTitleView.setAlpha(alpha); + mBubbleDismissIconView.setAlpha(alpha); + } + + private void setMenuItemViewsAlpha(float alpha) { + for (int i = mActionsSectionView.getChildCount() - 1; i >= 0; i--) { + mActionsSectionView.getChildAt(i).setAlpha(alpha); + } + } + /** Update menu details with bubble info */ void updateInfo(Bubble bubble) { if (bubble.getIcon() != null) { @@ -153,6 +186,11 @@ public class BubbleBarMenuView extends LinearLayout { return mBubbleSectionView.getAlpha(); } + /** Return title menu item height. */ + public float getTitleItemHeight() { + return mBubbleSectionView.getHeight(); + } + /** * Menu action details used to create menu items */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java index 514810745e10..9dd0cae20370 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java @@ -15,6 +15,9 @@ */ package com.android.wm.shell.bubbles.bar; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; @@ -26,13 +29,10 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import androidx.dynamicanimation.animation.DynamicAnimation; -import androidx.dynamicanimation.animation.SpringForce; - +import com.android.app.animation.Interpolators; import com.android.wm.shell.Flags; import com.android.wm.shell.R; import com.android.wm.shell.bubbles.Bubble; -import com.android.wm.shell.shared.animation.PhysicsAnimator; import java.util.ArrayList; @@ -40,22 +40,26 @@ import java.util.ArrayList; * Manages bubble bar expanded view menu presentation and animations */ class BubbleBarMenuViewController { - private static final float MENU_INITIAL_SCALE = 0.5f; + + private static final float WIDTH_SWAP_FRACTION = 0.4F; + private static final long MENU_ANIMATION_DURATION = 600; + private final Context mContext; private final ViewGroup mRootView; + private final BubbleBarHandleView mHandleView; private @Nullable Listener mListener; private @Nullable Bubble mBubble; private @Nullable BubbleBarMenuView mMenuView; /** A transparent view used to intercept touches to collapse menu when presented */ private @Nullable View mScrimView; - private @Nullable PhysicsAnimator<BubbleBarMenuView> mMenuAnimator; - private PhysicsAnimator.SpringConfig mMenuSpringConfig; + private @Nullable ValueAnimator mMenuAnimator; + - BubbleBarMenuViewController(Context context, ViewGroup rootView) { + BubbleBarMenuViewController(Context context, BubbleBarHandleView handleView, + ViewGroup rootView) { mContext = context; mRootView = rootView; - mMenuSpringConfig = new PhysicsAnimator.SpringConfig( - SpringForce.STIFFNESS_MEDIUM, SpringForce.DAMPING_RATIO_LOW_BOUNCY); + mHandleView = handleView; } /** Tells if the menu is visible or being animated */ @@ -81,20 +85,21 @@ class BubbleBarMenuViewController { if (mMenuView == null || mScrimView == null) { setupMenu(); } - cancelAnimations(); - mMenuView.setVisibility(View.VISIBLE); - mScrimView.setVisibility(View.VISIBLE); - Runnable endActions = () -> { - mMenuView.getChildAt(0).requestAccessibilityFocus(); - if (mListener != null) { - mListener.onMenuVisibilityChanged(true /* isShown */); + runOnMenuIsMeasured(() -> { + mMenuView.setVisibility(View.VISIBLE); + mScrimView.setVisibility(View.VISIBLE); + Runnable endActions = () -> { + mMenuView.getChildAt(0).requestAccessibilityFocus(); + if (mListener != null) { + mListener.onMenuVisibilityChanged(true /* isShown */); + } + }; + if (animated) { + animateTransition(true /* show */, endActions); + } else { + endActions.run(); } - }; - if (animated) { - animateTransition(true /* show */, endActions); - } else { - endActions.run(); - } + }); } /** @@ -103,18 +108,30 @@ class BubbleBarMenuViewController { */ void hideMenu(boolean animated) { if (mMenuView == null || mScrimView == null) return; - cancelAnimations(); - Runnable endActions = () -> { - mMenuView.setVisibility(View.GONE); - mScrimView.setVisibility(View.GONE); - if (mListener != null) { - mListener.onMenuVisibilityChanged(false /* isShown */); + runOnMenuIsMeasured(() -> { + Runnable endActions = () -> { + mHandleView.restoreAnimationDefaults(); + mMenuView.setVisibility(View.GONE); + mScrimView.setVisibility(View.GONE); + mHandleView.setVisibility(View.VISIBLE); + if (mListener != null) { + mListener.onMenuVisibilityChanged(false /* isShown */); + } + }; + if (animated) { + animateTransition(false /* show */, endActions); + } else { + endActions.run(); } - }; - if (animated) { - animateTransition(false /* show */, endActions); + }); + } + + private void runOnMenuIsMeasured(Runnable action) { + if (mMenuView.getWidth() == 0 || mMenuView.getHeight() == 0) { + // the menu view is not yet measured, postpone showing the animation + mMenuView.post(() -> runOnMenuIsMeasured(action)); } else { - endActions.run(); + action.run(); } } @@ -125,24 +142,63 @@ class BubbleBarMenuViewController { */ private void animateTransition(boolean show, Runnable endActions) { if (mMenuView == null) return; - mMenuAnimator = PhysicsAnimator.getInstance(mMenuView); - mMenuAnimator.setDefaultSpringConfig(mMenuSpringConfig); - mMenuAnimator - .spring(DynamicAnimation.ALPHA, show ? 1f : 0f) - .spring(DynamicAnimation.SCALE_Y, show ? 1f : MENU_INITIAL_SCALE) - .withEndActions(() -> { - mMenuAnimator = null; - endActions.run(); - }) - .start(); + float startValue = show ? 0 : 1; + if (mMenuAnimator != null && mMenuAnimator.isRunning()) { + startValue = (float) mMenuAnimator.getAnimatedValue(); + mMenuAnimator.cancel(); + } + ValueAnimator showMenuAnimation = ValueAnimator.ofFloat(startValue, show ? 1 : 0); + showMenuAnimation.setDuration(MENU_ANIMATION_DURATION); + showMenuAnimation.setInterpolator(Interpolators.EMPHASIZED); + showMenuAnimation.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mMenuAnimator = null; + endActions.run(); + } + }); + mMenuAnimator = showMenuAnimation; + setupAnimatorListener(showMenuAnimation); + showMenuAnimation.start(); } - /** Cancel running animations */ - private void cancelAnimations() { - if (mMenuAnimator != null) { - mMenuAnimator.cancel(); - mMenuAnimator = null; + /** Setup listener that orchestrates the animation. */ + private void setupAnimatorListener(ValueAnimator showMenuAnimation) { + // Getting views properties start values + int widthDiff = mMenuView.getWidth() - mHandleView.getHandleWidth(); + int handleHeight = mHandleView.getHandleHeight(); + float targetWidth = mHandleView.getHandleWidth() + widthDiff * WIDTH_SWAP_FRACTION; + float targetHeight = targetWidth * mMenuView.getTitleItemHeight() / mMenuView.getWidth(); + int menuColor; + try (TypedArray ta = mContext.obtainStyledAttributes(new int[]{ + com.android.internal.R.attr.materialColorSurfaceBright, + })) { + menuColor = ta.getColor(0, Color.WHITE); } + // Calculating deltas + float swapScale = targetWidth / mMenuView.getWidth(); + float handleWidthDelta = targetWidth - mHandleView.getHandleWidth(); + float handleHeightDelta = targetHeight - handleHeight; + // Setting update listener that will orchestrate the animation + showMenuAnimation.addUpdateListener(animator -> { + float animationProgress = (float) animator.getAnimatedValue(); + boolean showHandle = animationProgress <= WIDTH_SWAP_FRACTION; + mHandleView.setVisibility(showHandle ? View.VISIBLE : View.GONE); + mMenuView.setVisibility(showHandle ? View.GONE : View.VISIBLE); + if (showHandle) { + float handleAnimationProgress = animationProgress / WIDTH_SWAP_FRACTION; + mHandleView.animateHandleForMenu(handleAnimationProgress, handleWidthDelta, + handleHeightDelta, menuColor); + } else { + mMenuView.setTranslationY(mHandleView.getHandlePaddingTop()); + mMenuView.setPivotY(0); + mMenuView.setPivotX((float) mMenuView.getWidth() / 2); + float menuAnimationProgress = + (animationProgress - WIDTH_SWAP_FRACTION) / (1 - WIDTH_SWAP_FRACTION); + float currentMenuScale = swapScale + (1 - swapScale) * menuAnimationProgress; + mMenuView.animateFromStartScale(currentMenuScale, menuAnimationProgress); + } + }); } /** Sets up and inflate menu views */ @@ -150,9 +206,6 @@ class BubbleBarMenuViewController { // Menu view setup mMenuView = (BubbleBarMenuView) LayoutInflater.from(mContext).inflate( R.layout.bubble_bar_menu_view, mRootView, false); - mMenuView.setAlpha(0f); - mMenuView.setPivotY(0f); - mMenuView.setScaleY(MENU_INITIAL_SCALE); mMenuView.setOnCloseListener(() -> hideMenu(true /* animated */)); if (mBubble != null) { mMenuView.updateInfo(mBubble); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipAppOpsListener.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipAppOpsListener.kt index 4abb35c2a428..193c593e2ab2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipAppOpsListener.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipAppOpsListener.kt @@ -16,8 +16,11 @@ package com.android.wm.shell.common.pip import android.app.AppOpsManager +import android.content.ComponentName import android.content.Context import android.content.pm.PackageManager +import android.util.Pair +import com.android.internal.annotations.VisibleForTesting import com.android.wm.shell.common.ShellExecutor class PipAppOpsListener( @@ -27,10 +30,12 @@ class PipAppOpsListener( ) { private val mAppOpsManager: AppOpsManager = checkNotNull( mContext.getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager) + private var mTopPipActivityInfoSupplier: (Context) -> Pair<ComponentName?, Int> = + PipUtils::getTopPipActivity private val mAppOpsChangedListener = AppOpsManager.OnOpChangedListener { _, packageName -> try { // Dismiss the PiP once the user disables the app ops setting for that package - val topPipActivityInfo = PipUtils.getTopPipActivity(mContext) + val topPipActivityInfo = mTopPipActivityInfoSupplier.invoke(mContext) val componentName = topPipActivityInfo.first ?: return@OnOpChangedListener val userId = topPipActivityInfo.second val appInfo = mContext.packageManager @@ -75,4 +80,9 @@ class PipAppOpsListener( /** Dismisses the PIP window. */ fun dismissPip() } + + @VisibleForTesting + fun setTopPipActivityInfoSupplier(supplier: (Context) -> Pair<ComponentName?, Int>) { + mTopPipActivityInfoSupplier = supplier + } }
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java index 0200e18b5c50..c99d9ba862c1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java @@ -188,6 +188,8 @@ public class CompatUIController implements OnDisplaysChangedListener, */ private boolean mIsFirstReachabilityEducationRunning; + private boolean mIsInDesktopMode; + @NonNull private final CompatUIStatusManager mCompatUIStatusManager; @@ -253,18 +255,19 @@ public class CompatUIController implements OnDisplaysChangedListener, if (taskInfo != null && !taskInfo.appCompatTaskInfo.isTopActivityInSizeCompat()) { mSetOfTaskIdsShowingRestartDialog.remove(taskInfo.taskId); } - - if (taskInfo != null && taskListener != null) { - updateActiveTaskInfo(taskInfo); - } - - // We close all the Compat UI educations in case we're in desktop mode. - if (taskInfo.configuration == null || taskListener == null - || isInDesktopMode(taskInfo.displayId)) { + mIsInDesktopMode = isInDesktopMode(taskInfo); + // We close all the Compat UI educations in case TaskInfo has no configuration or + // TaskListener or in desktop mode. + if (taskInfo.configuration == null || taskListener == null || mIsInDesktopMode) { // Null token means the current foreground activity is not in compatibility mode. removeLayouts(taskInfo.taskId); return; } + if (taskInfo != null && taskListener != null) { + updateActiveTaskInfo(taskInfo); + } + + // We're showing the first reachability education so we ignore incoming TaskInfo // until the education flow has completed or we double tap. The double-tap // basically cancel all the onboarding flow. We don't have to ignore events in case @@ -443,7 +446,7 @@ public class CompatUIController implements OnDisplaysChangedListener, @Nullable ShellTaskOrganizer.TaskListener taskListener) { CompatUIWindowManager layout = mActiveCompatLayouts.get(taskInfo.taskId); if (layout != null) { - if (layout.needsToBeRecreated(taskInfo, taskListener)) { + if (layout.needsToBeRecreated(taskInfo, taskListener) || mIsInDesktopMode) { mActiveCompatLayouts.remove(taskInfo.taskId); layout.release(); } else { @@ -456,7 +459,10 @@ public class CompatUIController implements OnDisplaysChangedListener, return; } } - + if (mIsInDesktopMode) { + // Return if in desktop mode. + return; + } // Create a new UI layout. final Context context = getOrCreateDisplayContext(taskInfo.displayId); if (context == null) { @@ -494,7 +500,8 @@ public class CompatUIController implements OnDisplaysChangedListener, private void createOrUpdateLetterboxEduLayout(@NonNull TaskInfo taskInfo, @Nullable ShellTaskOrganizer.TaskListener taskListener) { if (mActiveLetterboxEduLayout != null) { - if (mActiveLetterboxEduLayout.needsToBeRecreated(taskInfo, taskListener)) { + if (mActiveLetterboxEduLayout.needsToBeRecreated(taskInfo, taskListener) + || mIsInDesktopMode) { mActiveLetterboxEduLayout.release(); mActiveLetterboxEduLayout = null; } else { @@ -507,6 +514,10 @@ public class CompatUIController implements OnDisplaysChangedListener, return; } } + if (mIsInDesktopMode) { + // Return if in desktop mode. + return; + } // Create a new UI layout. final Context context = getOrCreateDisplayContext(taskInfo.displayId); if (context == null) { @@ -541,7 +552,7 @@ public class CompatUIController implements OnDisplaysChangedListener, RestartDialogWindowManager layout = mTaskIdToRestartDialogWindowManagerMap.get(taskInfo.taskId); if (layout != null) { - if (layout.needsToBeRecreated(taskInfo, taskListener)) { + if (layout.needsToBeRecreated(taskInfo, taskListener) || mIsInDesktopMode) { mTaskIdToRestartDialogWindowManagerMap.remove(taskInfo.taskId); layout.release(); } else { @@ -556,6 +567,10 @@ public class CompatUIController implements OnDisplaysChangedListener, return; } } + if (mIsInDesktopMode) { + // Return if in desktop mode. + return; + } // Create a new UI layout. final Context context = getOrCreateDisplayContext(taskInfo.displayId); if (context == null) { @@ -594,7 +609,8 @@ public class CompatUIController implements OnDisplaysChangedListener, private void createOrUpdateReachabilityEduLayout(@NonNull TaskInfo taskInfo, @Nullable ShellTaskOrganizer.TaskListener taskListener) { if (mActiveReachabilityEduLayout != null) { - if (mActiveReachabilityEduLayout.needsToBeRecreated(taskInfo, taskListener)) { + if (mActiveReachabilityEduLayout.needsToBeRecreated(taskInfo, taskListener) + || mIsInDesktopMode) { mActiveReachabilityEduLayout.release(); mActiveReachabilityEduLayout = null; } else { @@ -608,6 +624,10 @@ public class CompatUIController implements OnDisplaysChangedListener, return; } } + if (mIsInDesktopMode) { + // Return if in desktop mode. + return; + } // Create a new UI layout. final Context context = getOrCreateDisplayContext(taskInfo.displayId); if (context == null) { @@ -647,7 +667,8 @@ public class CompatUIController implements OnDisplaysChangedListener, private void createOrUpdateUserAspectRatioSettingsLayout(@NonNull TaskInfo taskInfo, @Nullable ShellTaskOrganizer.TaskListener taskListener) { if (mUserAspectRatioSettingsLayout != null) { - if (mUserAspectRatioSettingsLayout.needsToBeRecreated(taskInfo, taskListener)) { + if (mUserAspectRatioSettingsLayout.needsToBeRecreated(taskInfo, taskListener) + || mIsInDesktopMode) { mUserAspectRatioSettingsLayout.release(); mUserAspectRatioSettingsLayout = null; } else { @@ -660,7 +681,10 @@ public class CompatUIController implements OnDisplaysChangedListener, return; } } - + if (mIsInDesktopMode) { + // Return if in desktop mode. + return; + } // Create a new UI layout. final Context context = getOrCreateDisplayContext(taskInfo.displayId); if (context == null) { @@ -840,8 +864,8 @@ public class CompatUIController implements OnDisplaysChangedListener, boolean mHasShownUserAspectRatioSettingsButtonHint; } - private boolean isInDesktopMode(int displayId) { - return Flags.skipCompatUiEducationInDesktopMode() - && mInDesktopModePredicate.test(displayId); + private boolean isInDesktopMode(@Nullable TaskInfo taskInfo) { + return taskInfo != null && Flags.skipCompatUiEducationInDesktopMode() + && mInDesktopModePredicate.test(taskInfo.displayId); } } 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 a472f79c98e6..601cf70b93ed 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 @@ -67,6 +67,7 @@ import com.android.wm.shell.dagger.pip.PipModule; import com.android.wm.shell.desktopmode.CloseDesktopTaskTransitionHandler; import com.android.wm.shell.desktopmode.DefaultDragToDesktopTransitionHandler; import com.android.wm.shell.desktopmode.DesktopActivityOrientationChangeHandler; +import com.android.wm.shell.desktopmode.DesktopBackNavigationTransitionHandler; import com.android.wm.shell.desktopmode.DesktopDisplayEventHandler; import com.android.wm.shell.desktopmode.DesktopImmersiveController; import com.android.wm.shell.desktopmode.DesktopMixedTransitionHandler; @@ -836,14 +837,21 @@ public abstract class WMShellModule { @Provides static Optional<DesktopImmersiveController> provideDesktopImmersiveController( Context context, + ShellInit shellInit, Transitions transitions, @DynamicOverride DesktopRepository desktopRepository, DisplayController displayController, - ShellTaskOrganizer shellTaskOrganizer) { + ShellTaskOrganizer shellTaskOrganizer, + ShellCommandHandler shellCommandHandler) { if (DesktopModeStatus.canEnterDesktopMode(context)) { return Optional.of( new DesktopImmersiveController( - transitions, desktopRepository, displayController, shellTaskOrganizer)); + shellInit, + transitions, + desktopRepository, + displayController, + shellTaskOrganizer, + shellCommandHandler)); } return Optional.empty(); } @@ -908,6 +916,16 @@ public abstract class WMShellModule { @WMSingleton @Provides + static DesktopBackNavigationTransitionHandler provideDesktopBackNavigationTransitionHandler( + @ShellMainThread ShellExecutor mainExecutor, + @ShellAnimationThread ShellExecutor animExecutor, + DisplayController displayController) { + return new DesktopBackNavigationTransitionHandler(mainExecutor, animExecutor, + displayController); + } + + @WMSingleton + @Provides static DesktopModeDragAndDropTransitionHandler provideDesktopModeDragAndDropTransitionHandler( Transitions transitions) { return new DesktopModeDragAndDropTransitionHandler(transitions); @@ -957,6 +975,7 @@ public abstract class WMShellModule { Optional<DesktopRepository> desktopRepository, Transitions transitions, ShellTaskOrganizer shellTaskOrganizer, + Optional<DesktopMixedTransitionHandler> desktopMixedTransitionHandler, ShellInit shellInit) { return desktopRepository.flatMap( repository -> @@ -966,6 +985,7 @@ public abstract class WMShellModule { repository, transitions, shellTaskOrganizer, + desktopMixedTransitionHandler.get(), shellInit))); } @@ -978,6 +998,7 @@ public abstract class WMShellModule { FreeformTaskTransitionHandler freeformTaskTransitionHandler, CloseDesktopTaskTransitionHandler closeDesktopTaskTransitionHandler, Optional<DesktopImmersiveController> desktopImmersiveController, + DesktopBackNavigationTransitionHandler desktopBackNavigationTransitionHandler, InteractionJankMonitor interactionJankMonitor, @ShellMainThread Handler handler, ShellInit shellInit, @@ -994,6 +1015,7 @@ public abstract class WMShellModule { freeformTaskTransitionHandler, closeDesktopTaskTransitionHandler, desktopImmersiveController.get(), + desktopBackNavigationTransitionHandler, interactionJankMonitor, handler, shellInit, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java index 3a4764d45f2c..3cd5df3121c1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java @@ -19,6 +19,7 @@ package com.android.wm.shell.dagger.pip; 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.WindowManagerShellWrapper; import com.android.wm.shell.common.DisplayController; @@ -41,6 +42,7 @@ import com.android.wm.shell.common.pip.PipUiEventLogger; 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.onehanded.OneHandedController; import com.android.wm.shell.pip.PipAnimationController; import com.android.wm.shell.pip.PipParamsChangedForwarder; @@ -169,6 +171,8 @@ public abstract class Pip1Module { PipParamsChangedForwarder pipParamsChangedForwarder, Optional<SplitScreenController> splitScreenControllerOptional, Optional<PipPerfHintController> pipPerfHintControllerOptional, + Optional<DesktopRepository> desktopRepositoryOptional, + RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer, DisplayController displayController, PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer, @ShellMainThread ShellExecutor mainExecutor) { @@ -176,7 +180,8 @@ public abstract class Pip1Module { syncTransactionQueue, pipTransitionState, pipBoundsState, pipDisplayLayoutState, pipBoundsAlgorithm, menuPhoneController, pipAnimationController, pipSurfaceTransactionHelper, pipTransitionController, pipParamsChangedForwarder, - splitScreenControllerOptional, pipPerfHintControllerOptional, displayController, + splitScreenControllerOptional, pipPerfHintControllerOptional, + desktopRepositoryOptional, rootTaskDisplayAreaOrganizer, displayController, pipUiEventLogger, shellTaskOrganizer, mainExecutor); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/TvPipModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/TvPipModule.java index 8d1b15c1e631..78e676f8cd45 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/TvPipModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/TvPipModule.java @@ -22,6 +22,7 @@ import android.os.SystemClock; import androidx.annotation.NonNull; +import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.WindowManagerShellWrapper; import com.android.wm.shell.common.DisplayController; @@ -214,6 +215,7 @@ public abstract class TvPipModule { PipSurfaceTransactionHelper pipSurfaceTransactionHelper, Optional<SplitScreenController> splitScreenControllerOptional, Optional<PipPerfHintController> pipPerfHintControllerOptional, + RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer, DisplayController displayController, PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer, @ShellMainThread ShellExecutor mainExecutor) { @@ -221,8 +223,9 @@ public abstract class TvPipModule { syncTransactionQueue, pipTransitionState, tvPipBoundsState, pipDisplayLayoutState, tvPipBoundsAlgorithm, tvPipMenuController, pipAnimationController, pipSurfaceTransactionHelper, tvPipTransition, pipParamsChangedForwarder, - splitScreenControllerOptional, pipPerfHintControllerOptional, displayController, - pipUiEventLogger, shellTaskOrganizer, mainExecutor); + splitScreenControllerOptional, pipPerfHintControllerOptional, + rootTaskDisplayAreaOrganizer, displayController, pipUiEventLogger, + shellTaskOrganizer, mainExecutor); } @WMSingleton diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopBackNavigationTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopBackNavigationTransitionHandler.kt new file mode 100644 index 000000000000..83b0f8413a28 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopBackNavigationTransitionHandler.kt @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.desktopmode + +import android.animation.Animator +import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM +import android.os.IBinder +import android.util.DisplayMetrics +import android.view.SurfaceControl.Transaction +import android.window.TransitionInfo +import android.window.TransitionRequestInfo +import android.window.WindowContainerTransaction +import com.android.wm.shell.common.DisplayController +import com.android.wm.shell.common.ShellExecutor +import com.android.wm.shell.shared.TransitionUtil +import com.android.wm.shell.shared.animation.MinimizeAnimator.create +import com.android.wm.shell.transition.Transitions + +/** + * The [Transitions.TransitionHandler] that handles transitions for tasks that are closing or going + * to back as part of back navigation. This handler is used only for animating transitions. + */ +class DesktopBackNavigationTransitionHandler( + private val mainExecutor: ShellExecutor, + private val animExecutor: ShellExecutor, + private val displayController: DisplayController, +) : Transitions.TransitionHandler { + + /** Shouldn't handle anything */ + override fun handleRequest( + transition: IBinder, + request: TransitionRequestInfo, + ): WindowContainerTransaction? = null + + /** Animates a transition with minimizing tasks */ + override fun startAnimation( + transition: IBinder, + info: TransitionInfo, + startTransaction: Transaction, + finishTransaction: Transaction, + finishCallback: Transitions.TransitionFinishCallback, + ): Boolean { + if (!TransitionUtil.isClosingType(info.type)) return false + + val animations = mutableListOf<Animator>() + val onAnimFinish: (Animator) -> Unit = { animator -> + mainExecutor.execute { + // Animation completed + animations.remove(animator) + if (animations.isEmpty()) { + // All animations completed, finish the transition + finishCallback.onTransitionFinished(/* wct= */ null) + } + } + } + + animations += + info.changes + .filter { + it.mode == info.type && + it.taskInfo?.windowingMode == WINDOWING_MODE_FREEFORM + } + .mapNotNull { createMinimizeAnimation(it, finishTransaction, onAnimFinish) } + if (animations.isEmpty()) return false + animExecutor.execute { animations.forEach(Animator::start) } + return true + } + + private fun createMinimizeAnimation( + change: TransitionInfo.Change, + finishTransaction: Transaction, + onAnimFinish: (Animator) -> Unit + ): Animator? { + val t = Transaction() + val sc = change.leash + finishTransaction.hide(sc) + val displayMetrics: DisplayMetrics? = + change.taskInfo?.let { + displayController.getDisplayContext(it.displayId)?.getResources()?.displayMetrics + } + return displayMetrics?.let { create(it, change, t, onAnimFinish) } + } +} 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 f69aa6df6a1d..1acde73e68dc 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 @@ -34,10 +34,13 @@ import com.android.window.flags.Flags import com.android.wm.shell.ShellTaskOrganizer import com.android.wm.shell.common.DisplayController import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE +import com.android.wm.shell.sysui.ShellCommandHandler +import com.android.wm.shell.sysui.ShellInit import com.android.wm.shell.transition.Transitions import com.android.wm.shell.transition.Transitions.TransitionHandler import com.android.wm.shell.transition.Transitions.TransitionObserver import com.android.wm.shell.windowdecor.OnTaskResizeAnimationListener +import java.io.PrintWriter /** * A controller to move tasks in/out of desktop's full immersive state where the task @@ -45,27 +48,34 @@ import com.android.wm.shell.windowdecor.OnTaskResizeAnimationListener * be transient below the status bar like in fullscreen immersive mode. */ class DesktopImmersiveController( + shellInit: ShellInit, private val transitions: Transitions, private val desktopRepository: DesktopRepository, private val displayController: DisplayController, private val shellTaskOrganizer: ShellTaskOrganizer, + private val shellCommandHandler: ShellCommandHandler, private val transactionSupplier: () -> SurfaceControl.Transaction, ) : TransitionHandler, TransitionObserver { constructor( + shellInit: ShellInit, transitions: Transitions, desktopRepository: DesktopRepository, displayController: DisplayController, shellTaskOrganizer: ShellTaskOrganizer, + shellCommandHandler: ShellCommandHandler, ) : this( + shellInit, transitions, desktopRepository, displayController, shellTaskOrganizer, + shellCommandHandler, { SurfaceControl.Transaction() } ) - private var state: TransitionState? = null + @VisibleForTesting + var state: TransitionState? = null @VisibleForTesting val pendingExternalExitTransitions = mutableListOf<ExternalPendingExit>() @@ -79,10 +89,21 @@ class DesktopImmersiveController( /** A listener to invoke on animation changes during entry/exit. */ var onTaskResizeAnimationListener: OnTaskResizeAnimationListener? = null + init { + shellInit.addInitCallback({ onInit() }, this) + } + + fun onInit() { + shellCommandHandler.addDumpCallback(this::dump, this) + } + /** Starts a transition to enter full immersive state inside the desktop. */ fun moveTaskToImmersive(taskInfo: RunningTaskInfo) { if (inProgress) { - logV("Cannot start entry because transition already in progress.") + logV( + "Cannot start entry because transition(s) already in progress: %s", + getRunningTransitions() + ) return } val wct = WindowContainerTransaction().apply { @@ -100,7 +121,10 @@ class DesktopImmersiveController( fun moveTaskToNonImmersive(taskInfo: RunningTaskInfo) { if (inProgress) { - logV("Cannot start exit because transition already in progress.") + logV( + "Cannot start exit because transition(s) already in progress: %s", + getRunningTransitions() + ) return } @@ -225,14 +249,19 @@ class DesktopImmersiveController( finishCallback: Transitions.TransitionFinishCallback ): Boolean { val state = requireState() - if (transition != state.transition) return false + check(state.transition == transition) { + "Transition $transition did not match expected state=$state" + } logD("startAnimation transition=%s", transition) animateResize( targetTaskId = state.taskId, info = info, startTransaction = startTransaction, finishTransaction = finishTransaction, - finishCallback = finishCallback + finishCallback = { + finishCallback.onTransitionFinished(/* wct= */ null) + clearState() + }, ) return true } @@ -242,12 +271,18 @@ class DesktopImmersiveController( info: TransitionInfo, startTransaction: SurfaceControl.Transaction, finishTransaction: SurfaceControl.Transaction, - finishCallback: Transitions.TransitionFinishCallback + finishCallback: Transitions.TransitionFinishCallback, ) { logD("animateResize for task#%d", targetTaskId) - val change = info.changes.first { c -> + val change = info.changes.firstOrNull { c -> val taskInfo = c.taskInfo - return@first taskInfo != null && taskInfo.taskId == targetTaskId + return@firstOrNull taskInfo != null && taskInfo.taskId == targetTaskId + } + if (change == null) { + logD("Did not find change for task#%d to animate", targetTaskId) + startTransaction.apply() + finishCallback.onTransitionFinished(/* wct= */ null) + return } animateResizeChange(change, startTransaction, finishTransaction, finishCallback) } @@ -288,7 +323,6 @@ class DesktopImmersiveController( .apply() onTaskResizeAnimationListener?.onAnimationEnd(taskId) finishCallback.onTransitionFinished(null /* wct */) - clearState() } ) addUpdateListener { animation -> @@ -357,8 +391,17 @@ class DesktopImmersiveController( // Check if this is a direct immersive enter/exit transition. if (transition == state?.transition) { val state = requireState() - val startBounds = info.changes.first { c -> c.taskInfo?.taskId == state.taskId } - .startAbsBounds + val immersiveChange = info.changes.firstOrNull { c -> + c.taskInfo?.taskId == state.taskId + } + if (immersiveChange == null) { + logV( + "Direct move for task#%d in %s direction missing immersive change.", + state.taskId, state.direction + ) + return + } + val startBounds = immersiveChange.startAbsBounds logV("Direct move for task#%d in %s direction verified", state.taskId, state.direction) when (state.direction) { Direction.ENTER -> { @@ -446,11 +489,30 @@ class DesktopImmersiveController( private fun requireState(): TransitionState = state ?: error("Expected non-null transition state") + private fun getRunningTransitions(): List<IBinder> { + val running = mutableListOf<IBinder>() + state?.let { + running.add(it.transition) + } + pendingExternalExitTransitions.forEach { + running.add(it.transition) + } + return running + } + private fun TransitionInfo.hasTaskChange(taskId: Int): Boolean = changes.any { c -> c.taskInfo?.taskId == taskId } + private fun dump(pw: PrintWriter, prefix: String) { + val innerPrefix = "$prefix " + pw.println("${prefix}DesktopImmersiveController") + pw.println(innerPrefix + "state=" + state) + pw.println(innerPrefix + "pendingExternalExitTransitions=" + pendingExternalExitTransitions) + } + /** The state of the currently running transition. */ - private data class TransitionState( + @VisibleForTesting + data class TransitionState( val transition: IBinder, val displayId: Int, val taskId: Int, @@ -483,7 +545,8 @@ class DesktopImmersiveController( fun asExit(): Exit? = if (this is Exit) this else null } - private enum class Direction { + @VisibleForTesting + enum class Direction { ENTER, EXIT } @@ -495,9 +558,10 @@ class DesktopImmersiveController( ProtoLog.d(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments) } - private companion object { + companion object { private const val TAG = "DesktopImmersive" - private const val FULL_IMMERSIVE_ANIM_DURATION_MS = 336L + @VisibleForTesting + const val FULL_IMMERSIVE_ANIM_DURATION_MS = 336L } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandler.kt index cefcb757690f..2001f9743094 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandler.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandler.kt @@ -52,6 +52,7 @@ class DesktopMixedTransitionHandler( private val freeformTaskTransitionHandler: FreeformTaskTransitionHandler, private val closeDesktopTaskTransitionHandler: CloseDesktopTaskTransitionHandler, private val desktopImmersiveController: DesktopImmersiveController, + private val desktopBackNavigationTransitionHandler: DesktopBackNavigationTransitionHandler, private val interactionJankMonitor: InteractionJankMonitor, @ShellMainThread private val handler: Handler, shellInit: ShellInit, @@ -161,6 +162,14 @@ class DesktopMixedTransitionHandler( finishTransaction, finishCallback ) + is PendingMixedTransition.Minimize -> animateMinimizeTransition( + pending, + transition, + info, + startTransaction, + finishTransaction, + finishCallback + ) } } @@ -205,11 +214,6 @@ class DesktopMixedTransitionHandler( finishTransaction: SurfaceControl.Transaction, finishCallback: TransitionFinishCallback, ): Boolean { - val launchChange = findDesktopTaskChange(info, pending.launchingTask) - if (launchChange == null) { - logV("No launch Change, returning") - return false - } // Check if there's also an immersive change during this launch. val immersiveExitChange = pending.exitingImmersiveTask?.let { exitingTask -> findDesktopTaskChange(info, exitingTask) @@ -217,6 +221,13 @@ class DesktopMixedTransitionHandler( val minimizeChange = pending.minimizingTask?.let { minimizingTask -> findDesktopTaskChange(info, minimizingTask) } + val launchChange = findDesktopTaskChange(info, pending.launchingTask) + if (launchChange == null) { + check(minimizeChange == null) + check(immersiveExitChange == null) + logV("No launch Change, returning") + return false + } var subAnimationCount = -1 var combinedWct: WindowContainerTransaction? = null @@ -270,6 +281,42 @@ class DesktopMixedTransitionHandler( ) } + private fun animateMinimizeTransition( + pending: PendingMixedTransition.Minimize, + transition: IBinder, + info: TransitionInfo, + startTransaction: SurfaceControl.Transaction, + finishTransaction: SurfaceControl.Transaction, + finishCallback: TransitionFinishCallback, + ): Boolean { + if (!DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION.isTrue) return false + + val minimizeChange = findDesktopTaskChange(info, pending.minimizingTask) + if (minimizeChange == null) { + logW("Should have minimizing desktop task") + return false + } + if (pending.isLastTask) { + // Dispatch close desktop task animation to the default transition handlers. + return dispatchToLeftoverHandler( + transition, + info, + startTransaction, + finishTransaction, + finishCallback + ) + } + + // Animate minimizing desktop task transition with [DesktopBackNavigationTransitionHandler]. + return desktopBackNavigationTransitionHandler.startAnimation( + transition, + info, + startTransaction, + finishTransaction, + finishCallback, + ) + } + override fun onTransitionConsumed( transition: IBinder, aborted: Boolean, @@ -398,6 +445,14 @@ class DesktopMixedTransitionHandler( val minimizingTask: Int?, val exitingImmersiveTask: Int?, ) : PendingMixedTransition() + + /** A task is minimizing. This should be used for task going to back and some closing cases + * with back navigation. */ + data class Minimize( + override val transition: IBinder, + val minimizingTask: Int, + val isLastTask: Boolean, + ) : PendingMixedTransition() } private fun logV(msg: String, vararg arguments: Any?) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt index bed484c7a532..39586e39fdd4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt @@ -594,6 +594,10 @@ class DesktopModeEventLogger { FrameworkStatsLog .DESKTOP_MODE_TASK_SIZE_UPDATED__RESIZE_TRIGGER__MAXIMIZE_MENU_RESIZE_TRIGGER ), + DRAG_TO_TOP_RESIZE_TRIGGER( + FrameworkStatsLog + .DESKTOP_MODE_TASK_SIZE_UPDATED__RESIZE_TRIGGER__DRAG_TO_TOP_RESIZE_TRIGGER + ), } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt index fda709a4a2d7..08ca55f93e3f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt @@ -102,6 +102,9 @@ class DesktopRepository ( /* Tracks last bounds of task before toggled to stable bounds. */ private val boundsBeforeMaximizeByTaskId = SparseArray<Rect>() + /* Tracks last bounds of task before it is minimized. */ + private val boundsBeforeMinimizeByTaskId = SparseArray<Rect>() + /* Tracks last bounds of task before toggled to immersive state. */ private val boundsBeforeFullImmersiveByTaskId = SparseArray<Rect>() @@ -462,6 +465,14 @@ class DesktopRepository ( fun saveBoundsBeforeMaximize(taskId: Int, bounds: Rect) = boundsBeforeMaximizeByTaskId.set(taskId, Rect(bounds)) + /** Removes and returns the bounds saved before minimizing the given task. */ + fun removeBoundsBeforeMinimize(taskId: Int): Rect? = + boundsBeforeMinimizeByTaskId.removeReturnOld(taskId) + + /** Saves the bounds of the given task before minimizing. */ + fun saveBoundsBeforeMinimize(taskId: Int, bounds: Rect?) = + boundsBeforeMinimizeByTaskId.set(taskId, Rect(bounds)) + /** Removes and returns the bounds saved before entering immersive with the given task. */ fun removeBoundsBeforeFullImmersive(taskId: Int): Rect? = boundsBeforeFullImmersiveByTaskId.removeReturnOld(taskId) 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 162879c97a16..6928c255edde 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 @@ -42,6 +42,7 @@ import android.os.Binder import android.os.Handler import android.os.IBinder import android.os.SystemProperties +import android.os.UserHandle import android.util.Size import android.view.Display.DEFAULT_DISPLAY import android.view.DragEvent @@ -871,11 +872,10 @@ class DesktopTasksController( return } - // TODO(b/375356605): Introduce a new ResizeTrigger for drag-to-top. desktopModeEventLogger.logTaskResizingStarted( - ResizeTrigger.UNKNOWN_RESIZE_TRIGGER, motionEvent, taskInfo, displayController + ResizeTrigger.DRAG_TO_TOP_RESIZE_TRIGGER, motionEvent, taskInfo, displayController ) - toggleDesktopTaskSize(taskInfo, ResizeTrigger.UNKNOWN_RESIZE_TRIGGER, motionEvent) + toggleDesktopTaskSize(taskInfo, ResizeTrigger.DRAG_TO_TOP_RESIZE_TRIGGER, motionEvent) } private fun getMaximizeBounds(taskInfo: RunningTaskInfo, stableBounds: Rect): Rect { @@ -1175,7 +1175,11 @@ class DesktopTasksController( private fun addWallpaperActivity(wct: WindowContainerTransaction) { logV("addWallpaperActivity") - val intent = Intent(context, DesktopWallpaperActivity::class.java) + val userHandle = UserHandle.of(userId) + val userContext = + context.createContextAsUser(userHandle, /* flags= */ 0) + val intent = Intent(userContext, DesktopWallpaperActivity::class.java) + intent.putExtra(Intent.EXTRA_USER_HANDLE, userId) val options = ActivityOptions.makeBasic().apply { launchWindowingMode = WINDOWING_MODE_FULLSCREEN @@ -1183,11 +1187,13 @@ class DesktopTasksController( ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS } val pendingIntent = - PendingIntent.getActivity( - context, - /* requestCode = */ 0, + PendingIntent.getActivityAsUser( + userContext, + /* requestCode= */ 0, intent, - PendingIntent.FLAG_IMMUTABLE + PendingIntent.FLAG_IMMUTABLE, + /* bundle= */ null, + userHandle ) wct.sendPendingIntent(pendingIntent, intent, options.toBundle()) } @@ -1291,7 +1297,11 @@ class DesktopTasksController( // Check if freeform task launch during recents should be handled shouldHandleMidRecentsFreeformLaunch -> handleMidRecentsFreeformTaskLaunch(task) // Check if the closing task needs to be handled - TransitionUtil.isClosingType(request.type) -> handleTaskClosing(task) + TransitionUtil.isClosingType(request.type) -> handleTaskClosing( + task, + transition, + request.type + ) // Check if the top task shouldn't be allowed to enter desktop mode isIncompatibleTask(task) -> handleIncompatibleTaskLaunch(task) // Check if fullscreen task should be updated @@ -1621,7 +1631,7 @@ class DesktopTasksController( } /** Handle task closing by removing wallpaper activity if it's the last active task */ - private fun handleTaskClosing(task: RunningTaskInfo): WindowContainerTransaction? { + private fun handleTaskClosing(task: RunningTaskInfo, transition: IBinder, requestType: Int): WindowContainerTransaction? { logV("handleTaskClosing") if (!isDesktopModeShowing(task.displayId)) return null @@ -1637,8 +1647,15 @@ class DesktopTasksController( if (!DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION.isTrue()) { taskRepository.addClosingTask(task.displayId, task.taskId) desktopTilingDecorViewModel.removeTaskIfTiled(task.displayId, task.taskId) + } else if (requestType == TRANSIT_CLOSE) { + // Handle closing tasks, tasks that are going to back are handled in + // [DesktopTasksTransitionObserver]. + desktopMixedTransitionHandler.addPendingMixedTransition( + DesktopMixedTransitionHandler.PendingMixedTransition.Minimize( + transition, task.taskId, taskRepository.getVisibleTaskCount(task.displayId) == 1 + ) + ) } - taskbarDesktopTaskListener?.onTaskbarCornerRoundingUpdate( doesAnyTaskRequireTaskbarRounding( task.displayId, @@ -1770,9 +1787,13 @@ class DesktopTasksController( transition: IBinder, taskIdToMinimize: Int, ) { - val taskToMinimize = shellTaskOrganizer.getRunningTaskInfo(taskIdToMinimize) ?: return + val taskToMinimize = shellTaskOrganizer.getRunningTaskInfo(taskIdToMinimize) desktopTasksLimiter.ifPresent { - it.addPendingMinimizeChange(transition, taskToMinimize.displayId, taskToMinimize.taskId) + it.addPendingMinimizeChange( + transition = transition, + displayId = taskToMinimize?.displayId ?: DEFAULT_DISPLAY, + taskId = taskIdToMinimize + ) } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt index f0e3a2bd8ffc..77af627a948a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt @@ -92,6 +92,12 @@ class DesktopTasksLimiter ( } taskToMinimize.transitionInfo = info activeTransitionTokensAndTasks[transition] = taskToMinimize + + // Save current bounds before minimizing in case we need to restore to it later. + val boundsBeforeMinimize = info.changes.find { change -> + change.taskInfo?.taskId == taskToMinimize.taskId }?.startAbsBounds + taskRepository.saveBoundsBeforeMinimize(taskToMinimize.taskId, boundsBeforeMinimize) + this@DesktopTasksLimiter.minimizeTask( taskToMinimize.displayId, taskToMinimize.taskId) } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt index d1534da9a078..c39c715e685c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt @@ -46,6 +46,7 @@ class DesktopTasksTransitionObserver( private val desktopRepository: DesktopRepository, private val transitions: Transitions, private val shellTaskOrganizer: ShellTaskOrganizer, + private val desktopMixedTransitionHandler: DesktopMixedTransitionHandler, shellInit: ShellInit ) : Transitions.TransitionObserver { @@ -71,7 +72,7 @@ class DesktopTasksTransitionObserver( // TODO: b/332682201 Update repository state updateWallpaperToken(info) if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION.isTrue()) { - handleBackNavigation(info) + handleBackNavigation(transition, info) removeTaskIfNeeded(info) } removeWallpaperOnLastTaskClosingIfNeeded(transition, info) @@ -95,7 +96,7 @@ class DesktopTasksTransitionObserver( } } - private fun handleBackNavigation(info: TransitionInfo) { + private fun handleBackNavigation(transition: IBinder, info: TransitionInfo) { // When default back navigation happens, transition type is TO_BACK and the change is // TO_BACK. Mark the task going to back as minimized. if (info.type == TRANSIT_TO_BACK) { @@ -105,10 +106,14 @@ class DesktopTasksTransitionObserver( continue } - if (desktopRepository.getVisibleTaskCount(taskInfo.displayId) > 0 && + val visibleTaskCount = desktopRepository.getVisibleTaskCount(taskInfo.displayId) + if (visibleTaskCount > 0 && change.mode == TRANSIT_TO_BACK && taskInfo.windowingMode == WINDOWING_MODE_FREEFORM) { desktopRepository.minimizeTask(taskInfo.displayId, taskInfo.taskId) + desktopMixedTransitionHandler.addPendingMixedTransition( + DesktopMixedTransitionHandler.PendingMixedTransition.Minimize( + transition, taskInfo.taskId, visibleTaskCount == 1)) } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopWallpaperActivity.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopWallpaperActivity.kt index 1c2415c236ad..e835b2fec232 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopWallpaperActivity.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopWallpaperActivity.kt @@ -36,7 +36,6 @@ import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE class DesktopWallpaperActivity : Activity() { override fun onCreate(savedInstanceState: Bundle?) { - ProtoLog.d(WM_SHELL_DESKTOP_MODE, "DesktopWallpaperActivity: onCreate") super.onCreate(savedInstanceState) window.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragSession.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragSession.java index dcbdfa349687..a611fe1db2ce 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragSession.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragSession.java @@ -17,6 +17,7 @@ package com.android.wm.shell.draganddrop; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; +import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.content.ClipDescription.EXTRA_HIDE_DRAG_SOURCE_TASK_ID; @@ -94,25 +95,30 @@ public class DragSession { void updateRunningTask() { final boolean hideDragSourceTask = hideDragSourceTaskId != -1; final List<ActivityManager.RunningTaskInfo> tasks = - mActivityTaskManager.getTasks(hideDragSourceTask ? 2 : 1, - false /* filterOnlyVisibleRecents */); - if (!tasks.isEmpty()) { - for (int i = tasks.size() - 1; i >= 0; i--) { - final ActivityManager.RunningTaskInfo task = tasks.get(i); - if (hideDragSourceTask && hideDragSourceTaskId == task.taskId) { - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, - "Skipping running task: id=%d component=%s", task.taskId, - task.baseIntent != null ? task.baseIntent.getComponent() : "null"); - continue; - } - runningTaskInfo = task; - runningTaskWinMode = task.getWindowingMode(); - runningTaskActType = task.getActivityType(); + mActivityTaskManager.getTasks(5, false /* filterOnlyVisibleRecents */); + for (int i = 0; i < tasks.size(); i++) { + final ActivityManager.RunningTaskInfo task = tasks.get(i); + if (hideDragSourceTask && hideDragSourceTaskId == task.taskId) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, - "Running task: id=%d component=%s", task.taskId, + "Skipping running task: id=%d component=%s", task.taskId, task.baseIntent != null ? task.baseIntent.getComponent() : "null"); - break; + continue; } + if (!task.isVisible) { + // Skip invisible tasks + continue; + } + if (task.configuration.windowConfiguration.isAlwaysOnTop()) { + // Skip always-on-top floating tasks + continue; + } + runningTaskInfo = task; + runningTaskWinMode = task.getWindowingMode(); + runningTaskActType = task.getActivityType(); + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, + "Running task: id=%d component=%s", task.taskId, + task.baseIntent != null ? task.baseIntent.getComponent() : "null"); + break; } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java index b27c428f1693..0154d0455e50 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java @@ -45,12 +45,17 @@ public interface Pip { } /** - * Set the callback when {@link PipTaskOrganizer#isInPip()} state is changed. + * Set the callback when isInPip state is changed. * - * @param callback The callback accepts the result of {@link PipTaskOrganizer#isInPip()} - * when it's changed. + * @param callback The callback accepts the state of isInPip when it's changed. */ - default void setOnIsInPipStateChangedListener(Consumer<Boolean> callback) {} + default void addOnIsInPipStateChangedListener(@NonNull Consumer<Boolean> callback) {} + + /** + * Remove the callback when isInPip state is changed. + * @param callback The callback accepts the state of isInPip when it's changed. + */ + default void removeOnIsInPipStateChangedListener(@NonNull Consumer<Boolean> callback) {} /** * Called when showing Pip menu. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java index 4aeecbec7dfb..5276d9d6a4df 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java @@ -27,6 +27,7 @@ import android.animation.RectEvaluator; import android.animation.ValueAnimator; import android.annotation.IntDef; import android.annotation.NonNull; +import android.app.AppCompatTaskInfo; import android.app.TaskInfo; import android.content.Context; import android.content.pm.ActivityInfo; @@ -176,12 +177,12 @@ public class PipAnimationController { public PipTransitionAnimator getAnimator(TaskInfo taskInfo, SurfaceControl leash, Rect baseBounds, Rect startBounds, Rect endBounds, Rect sourceHintRect, @PipAnimationController.TransitionDirection int direction, float startingAngle, - @Surface.Rotation int rotationDelta) { + @Surface.Rotation int rotationDelta, boolean alwaysAnimateTaskBounds) { if (mCurrentAnimator == null) { mCurrentAnimator = setupPipTransitionAnimator( PipTransitionAnimator.ofBounds(taskInfo, leash, startBounds, startBounds, endBounds, sourceHintRect, direction, 0 /* startingAngle */, - rotationDelta)); + rotationDelta, alwaysAnimateTaskBounds)); } else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_ALPHA && mCurrentAnimator.isRunning()) { // If we are still animating the fade into pip, then just move the surface and ensure @@ -197,7 +198,8 @@ public class PipAnimationController { mCurrentAnimator.cancel(); mCurrentAnimator = setupPipTransitionAnimator( PipTransitionAnimator.ofBounds(taskInfo, leash, baseBounds, startBounds, - endBounds, sourceHintRect, direction, startingAngle, rotationDelta)); + endBounds, sourceHintRect, direction, startingAngle, rotationDelta, + alwaysAnimateTaskBounds)); } return mCurrentAnimator; } @@ -585,28 +587,32 @@ public class PipAnimationController { static PipTransitionAnimator<Rect> ofBounds(TaskInfo taskInfo, SurfaceControl leash, Rect baseValue, Rect startValue, Rect endValue, Rect sourceRectHint, @PipAnimationController.TransitionDirection int direction, float startingAngle, - @Surface.Rotation int rotationDelta) { + @Surface.Rotation int rotationDelta, boolean alwaysAnimateTaskBounds) { final boolean isOutPipDirection = isOutPipDirection(direction); final boolean isInPipDirection = isInPipDirection(direction); // Just for simplicity we'll interpolate between the source rect hint insets and empty // insets to calculate the window crop final Rect initialSourceValue; final Rect mainWindowFrame = taskInfo.topActivityMainWindowFrame; - final boolean hasNonMatchFrame = mainWindowFrame != null; + final AppCompatTaskInfo compatInfo = taskInfo.appCompatTaskInfo; + final boolean isSizeCompatOrLetterboxed = compatInfo.isTopActivityInSizeCompat() + || compatInfo.isTopActivityLetterboxed(); + // For the animation to swipe PIP to home or restore a PIP task from home, we don't + // override to the main window frame since we should animate the whole task. + final boolean shouldUseMainWindowFrame = mainWindowFrame != null + && !alwaysAnimateTaskBounds && !isSizeCompatOrLetterboxed; final boolean changeOrientation = rotationDelta == ROTATION_90 || rotationDelta == ROTATION_270; final Rect baseBounds = new Rect(baseValue); final Rect startBounds = new Rect(startValue); final Rect endBounds = new Rect(endValue); if (isOutPipDirection) { - // TODO(b/356277166): handle rotation change with activity that provides main window - // frame. - if (hasNonMatchFrame && !changeOrientation) { + if (shouldUseMainWindowFrame && !changeOrientation) { endBounds.set(mainWindowFrame); } initialSourceValue = new Rect(endBounds); } else if (isInPipDirection) { - if (hasNonMatchFrame) { + if (shouldUseMainWindowFrame) { baseBounds.set(mainWindowFrame); if (startValue.equals(baseValue)) { // If the start value is at initial state as in PIP animation, also override @@ -635,9 +641,19 @@ public class PipAnimationController { if (changeOrientation) { lastEndRect = new Rect(endBounds); rotatedEndRect = new Rect(endBounds); - // Rotate the end bounds according to the rotation delta because the display will - // be rotated to the same orientation. - rotateBounds(rotatedEndRect, initialSourceValue, rotationDelta); + // TODO(b/375977163): polish the animation to restoring the PIP task back from + // swipe-pip-to-home. Ideally we should send the transitionInfo after reparenting + // the PIP activity back to the original task. + if (shouldUseMainWindowFrame) { + // If we should animate the main window frame, set it to the rotatedRect + // instead. The end bounds reported by transitionInfo is the bounds before + // rotation, while main window frame is calculated after the rotation. + rotatedEndRect.set(mainWindowFrame); + } else { + // Rotate the end bounds according to the rotation delta because the display + // will be rotated to the same orientation. + rotateBounds(rotatedEndRect, initialSourceValue, rotationDelta); + } // Use the rect that has the same orientation as the hint rect. initialContainerRect = isOutPipDirection ? rotatedEndRect : initialSourceValue; } else { 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 c4e63dfdade9..30f1948efa2d 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 @@ -17,6 +17,7 @@ package com.android.wm.shell.pip; import static android.app.ActivityTaskManager.INVALID_TASK_ID; +import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; @@ -67,6 +68,7 @@ import android.view.Choreographer; import android.view.Display; import android.view.Surface; import android.view.SurfaceControl; +import android.window.DisplayAreaInfo; import android.window.TaskOrganizer; import android.window.TaskSnapshot; import android.window.WindowContainerToken; @@ -74,7 +76,9 @@ import android.window.WindowContainerTransaction; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.ProtoLog; +import com.android.window.flags.Flags; import com.android.wm.shell.R; +import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.ScreenshotUtils; @@ -87,6 +91,7 @@ import com.android.wm.shell.common.pip.PipMenuController; import com.android.wm.shell.common.pip.PipPerfHintController; import com.android.wm.shell.common.pip.PipUiEventLogger; import com.android.wm.shell.common.pip.PipUtils; +import com.android.wm.shell.desktopmode.DesktopRepository; import com.android.wm.shell.pip.phone.PipMotionHelper; import com.android.wm.shell.protolog.ShellProtoLogGroup; import com.android.wm.shell.shared.animation.Interpolators; @@ -145,6 +150,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, private final PipSurfaceTransactionHelper mSurfaceTransactionHelper; private final Optional<SplitScreenController> mSplitScreenOptional; @Nullable private final PipPerfHintController mPipPerfHintController; + private final Optional<DesktopRepository> mDesktopRepositoryOptional; + private final RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer; protected final ShellTaskOrganizer mTaskOrganizer; protected final ShellExecutor mMainExecutor; @@ -388,6 +395,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, @NonNull PipParamsChangedForwarder pipParamsChangedForwarder, Optional<SplitScreenController> splitScreenOptional, Optional<PipPerfHintController> pipPerfHintControllerOptional, + Optional<DesktopRepository> desktopRepositoryOptional, + RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer, @NonNull DisplayController displayController, @NonNull PipUiEventLogger pipUiEventLogger, @NonNull ShellTaskOrganizer shellTaskOrganizer, @@ -414,6 +423,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, new PipSurfaceTransactionHelper.VsyncSurfaceControlTransactionFactory(); mSplitScreenOptional = splitScreenOptional; mPipPerfHintController = pipPerfHintControllerOptional.orElse(null); + mDesktopRepositoryOptional = desktopRepositoryOptional; + mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer; mTaskOrganizer = shellTaskOrganizer; mMainExecutor = mainExecutor; @@ -741,10 +752,23 @@ 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( + mTaskInfo.taskId); + return Objects.requireNonNullElseGet(freeformBounds, mPipBoundsState::getDisplayBounds); + } 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() { + return Flags.enableDesktopWindowingPip() && mDesktopRepositoryOptional.isPresent() + && mDesktopRepositoryOptional.get().isMinimizedTask(mTaskInfo.taskId); + } + private void exitLaunchIntoPipTask(WindowContainerTransaction wct) { wct.startTask(mTaskInfo.launchIntoPipHostTaskId, null /* ActivityOptions */); mTaskOrganizer.applyTransaction(wct); @@ -1808,7 +1832,25 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, * and can be overridden to restore to an alternate windowing mode. */ public int getOutPipWindowingMode() { - // By default, simply reset the windowing mode to undefined. + final DisplayAreaInfo tdaInfo = mRootTaskDisplayAreaOrganizer.getDisplayAreaInfo( + mTaskInfo.displayId); + + // If PiP was launched while in desktop mode (we should return the task 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) { + 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; } @@ -1838,9 +1880,11 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, ? mPipBoundsState.getBounds() : currentBounds; final boolean existingAnimatorRunning = mPipAnimationController.getCurrentAnimator() != null && mPipAnimationController.getCurrentAnimator().isRunning(); + // For resize animation, we always animate the whole PIP task bounds. final PipAnimationController.PipTransitionAnimator<?> animator = mPipAnimationController .getAnimator(mTaskInfo, mLeash, baseBounds, currentBounds, destinationBounds, - sourceHintRect, direction, startingAngle, rotationDelta); + sourceHintRect, direction, startingAngle, rotationDelta, + true /* alwaysAnimateTaskBounds */); animator.setTransitionDirection(direction) .setPipTransactionHandler(mPipTransactionHandler) .setDuration(durationMs); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java index 28b91c6cb812..f7aed4401247 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java @@ -530,6 +530,13 @@ public class PipTransition extends PipTransitionController { if (mFixedRotationState != FIXED_ROTATION_TRANSITION && mFinishTransaction != null) { mFinishTransaction.merge(tx); + // Set window crop and position to destination bounds to avoid flickering. + if (hasValidLeash) { + mFinishTransaction.setWindowCrop(leash, destinationBounds.width(), + destinationBounds.height()); + mFinishTransaction.setPosition(leash, destinationBounds.left, + destinationBounds.top); + } } } else { wct = new WindowContainerTransaction(); @@ -884,7 +891,8 @@ public class PipTransition extends PipTransitionController { final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController.getAnimator(taskInfo, pipChange.getLeash(), startBounds, startBounds, endBounds, null, TRANSITION_DIRECTION_LEAVE_PIP, - 0 /* startingAngle */, pipRotateDelta); + 0 /* startingAngle */, pipRotateDelta, + false /* alwaysAnimateTaskBounds */); animator.setTransitionDirection(TRANSITION_DIRECTION_LEAVE_PIP) .setPipAnimationCallback(mPipAnimationCallback) .setDuration(mEnterExitAnimationDuration) @@ -899,7 +907,7 @@ public class PipTransition extends PipTransitionController { final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController.getAnimator(taskInfo, leash, baseBounds, startBounds, endBounds, sourceHintRect, TRANSITION_DIRECTION_LEAVE_PIP, - 0 /* startingAngle */, rotationDelta); + 0 /* startingAngle */, rotationDelta, false /* alwaysAnimateTaskBounds */); animator.setTransitionDirection(TRANSITION_DIRECTION_LEAVE_PIP) .setDuration(mEnterExitAnimationDuration); if (startTransaction != null) { @@ -1095,8 +1103,6 @@ public class PipTransition extends PipTransitionController { if (taskInfo.pictureInPictureParams != null && taskInfo.pictureInPictureParams.isAutoEnterEnabled() && mPipTransitionState.getInSwipePipToHomeTransition()) { - // TODO(b/356277166): add support to swipe PIP to home with - // non-match parent activity. handleSwipePipToHomeTransition(startTransaction, finishTransaction, leash, sourceHintRect, destinationBounds, taskInfo); return; @@ -1118,7 +1124,7 @@ public class PipTransition extends PipTransitionController { if (enterAnimationType == ANIM_TYPE_BOUNDS) { animator = mPipAnimationController.getAnimator(taskInfo, leash, currentBounds, currentBounds, destinationBounds, sourceHintRect, TRANSITION_DIRECTION_TO_PIP, - 0 /* startingAngle */, rotationDelta); + 0 /* startingAngle */, rotationDelta, false /* alwaysAnimateTaskBounds */); if (sourceHintRect == null) { // We use content overlay when there is no source rect hint to enter PiP use bounds // animation. We also temporarily disallow app icon overlay and use color overlay @@ -1241,10 +1247,14 @@ public class PipTransition extends PipTransitionController { // to avoid flicker. final Rect savedDisplayCutoutInsets = new Rect(pipTaskInfo.displayCutoutInsets); pipTaskInfo.displayCutoutInsets.setEmpty(); + // Always use the task bounds even if the PIP activity doesn't match parent because the app + // and the whole task will move behind. We should animate the whole task bounds in this + // case. final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController.getAnimator(pipTaskInfo, leash, sourceBounds, sourceBounds, destinationBounds, sourceHintRect, TRANSITION_DIRECTION_TO_PIP, - 0 /* startingAngle */, ROTATION_0 /* rotationDelta */) + 0 /* startingAngle */, ROTATION_0 /* rotationDelta */, + true /* alwaysAnimateTaskBounds */) .setPipTransactionHandler(mTransactionConsumer) .setTransitionDirection(TRANSITION_DIRECTION_TO_PIP); // The start state is the end state for swipe-auto-pip. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java index 5ffc64f412f1..79a9ce5212c6 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java @@ -47,7 +47,6 @@ import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.pip.PipBoundsAlgorithm; import com.android.wm.shell.common.pip.PipBoundsState; import com.android.wm.shell.common.pip.PipMenuController; -import com.android.wm.shell.common.split.SplitScreenUtils; import com.android.wm.shell.protolog.ShellProtoLogGroup; import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.transition.DefaultMixedHandler; @@ -312,10 +311,10 @@ public abstract class PipTransitionController implements Transitions.TransitionH } /** Whether a particular package is same as current pip package. */ - public boolean isPackageActiveInPip(String packageName) { - final TaskInfo inPipTask = mPipOrganizer.getTaskInfo(); - return packageName != null && inPipTask != null && mPipOrganizer.isInPip() - && packageName.equals(SplitScreenUtils.getPackageName(inPipTask.baseIntent)); + public boolean isPackageActiveInPip(@Nullable String packageName) { + return packageName != null + && mPipBoundsState.getLastPipComponentName() != null + && packageName.equals(mPipBoundsState.getLastPipComponentName().getPackageName()); } /** Add PiP-related changes to `outWCT` for the given request. */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java index 7f6118689dad..588b88753eb9 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java @@ -104,6 +104,7 @@ import com.android.wm.shell.sysui.UserChangeListener; import com.android.wm.shell.transition.Transitions; import java.io.PrintWriter; +import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.Optional; @@ -215,7 +216,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb private boolean mIsKeyguardShowingOrAnimating; - private Consumer<Boolean> mOnIsInPipStateChangedListener; + private final List<Consumer<Boolean>> mOnIsInPipStateChangedListeners = new ArrayList<>(); @VisibleForTesting interface PipAnimationListener { @@ -501,11 +502,11 @@ public class PipController implements PipTransitionController.PipTransitionCallb false /* saveRestoreSnapFraction */); }); mPipTransitionState.addOnPipTransitionStateChangedListener((oldState, newState) -> { - if (mOnIsInPipStateChangedListener != null) { - final boolean wasInPip = PipTransitionState.isInPip(oldState); - final boolean nowInPip = PipTransitionState.isInPip(newState); - if (nowInPip != wasInPip) { - mOnIsInPipStateChangedListener.accept(nowInPip); + final boolean wasInPip = PipTransitionState.isInPip(oldState); + final boolean nowInPip = PipTransitionState.isInPip(newState); + if (nowInPip != wasInPip) { + for (Consumer<Boolean> listener : mOnIsInPipStateChangedListeners) { + listener.accept(nowInPip); } } }); @@ -960,13 +961,19 @@ public class PipController implements PipTransitionController.PipTransitionCallb mPipBoundsState.getLauncherState().setAppIconSizePx(iconSizePx); } - private void setOnIsInPipStateChangedListener(Consumer<Boolean> callback) { - mOnIsInPipStateChangedListener = callback; - if (mOnIsInPipStateChangedListener != null) { + private void addOnIsInPipStateChangedListener(@NonNull Consumer<Boolean> callback) { + if (callback != null) { + mOnIsInPipStateChangedListeners.add(callback); callback.accept(mPipTransitionState.isInPip()); } } + private void removeOnIsInPipStateChangedListener(@NonNull Consumer<Boolean> callback) { + if (callback != null) { + mOnIsInPipStateChangedListeners.remove(callback); + } + } + private void setShelfHeightLocked(boolean visible, int height) { final int shelfHeight = visible ? height : 0; mPipBoundsState.setShelfVisibility(visible, shelfHeight); @@ -1222,9 +1229,16 @@ public class PipController implements PipTransitionController.PipTransitionCallb } @Override - public void setOnIsInPipStateChangedListener(Consumer<Boolean> callback) { + public void addOnIsInPipStateChangedListener(@NonNull Consumer<Boolean> callback) { + mMainExecutor.execute(() -> { + PipController.this.addOnIsInPipStateChangedListener(callback); + }); + } + + @Override + public void removeOnIsInPipStateChangedListener(@NonNull Consumer<Boolean> callback) { mMainExecutor.execute(() -> { - PipController.this.setOnIsInPipStateChangedListener(callback); + PipController.this.removeOnIsInPipStateChangedListener(callback); }); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTaskOrganizer.java index 614ef2ab9831..fcba46108f67 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTaskOrganizer.java @@ -21,6 +21,7 @@ import android.content.Context; import androidx.annotation.NonNull; +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.ShellExecutor; @@ -61,6 +62,7 @@ public class TvPipTaskOrganizer extends PipTaskOrganizer { @NonNull PipParamsChangedForwarder pipParamsChangedForwarder, Optional<SplitScreenController> splitScreenOptional, Optional<PipPerfHintController> pipPerfHintControllerOptional, + @NonNull RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer, @NonNull DisplayController displayController, @NonNull PipUiEventLogger pipUiEventLogger, @NonNull ShellTaskOrganizer shellTaskOrganizer, @@ -68,8 +70,9 @@ public class TvPipTaskOrganizer extends PipTaskOrganizer { super(context, syncTransactionQueue, pipTransitionState, pipBoundsState, pipDisplayLayoutState, boundsHandler, pipMenuController, pipAnimationController, surfaceTransactionHelper, tvPipTransition, pipParamsChangedForwarder, - splitScreenOptional, pipPerfHintControllerOptional, displayController, - pipUiEventLogger, shellTaskOrganizer, mainExecutor); + splitScreenOptional, pipPerfHintControllerOptional, Optional.empty(), + rootTaskDisplayAreaOrganizer, displayController, pipUiEventLogger, + shellTaskOrganizer, mainExecutor); mTvPipTransition = tvPipTransition; } 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 d3f537b8f904..e901c39b8792 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 @@ -21,6 +21,7 @@ import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE; import static com.android.wm.shell.shared.ShellSharedConstants.KEY_EXTRA_SHELL_PIP; +import android.annotation.NonNull; import android.app.ActivityManager; import android.app.PictureInPictureParams; import android.content.ComponentName; @@ -66,6 +67,8 @@ import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.sysui.ShellInit; import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.List; import java.util.function.Consumer; /** @@ -94,7 +97,7 @@ public class PipController implements ConfigurationChangeListener, private final PipTouchHandler mPipTouchHandler; private final ShellExecutor mMainExecutor; private final PipImpl mImpl; - private Consumer<Boolean> mOnIsInPipStateChangedListener; + private final List<Consumer<Boolean>> mOnIsInPipStateChangedListeners = new ArrayList<>(); // Wrapper for making Binder calls into PiP animation listener hosted in launcher's Recents. private PipAnimationListener mPipRecentsAnimationListener; @@ -319,7 +322,7 @@ public class PipController implements ConfigurationChangeListener, mPipBoundsAlgorithm.applySnapFraction(toBounds, snapFraction); mPipBoundsState.setBounds(toBounds); } - t.setBounds(mPipTransitionState.mPipTaskToken, mPipBoundsState.getBounds()); + t.setBounds(mPipTransitionState.getPipTaskToken(), mPipBoundsState.getBounds()); } private void setDisplayLayout(DisplayLayout layout) { @@ -413,13 +416,13 @@ public class PipController implements ConfigurationChangeListener, if (mPipTransitionState.isInSwipePipToHomeTransition()) { mPipTransitionState.resetSwipePipToHomeState(); } - if (mOnIsInPipStateChangedListener != null) { - mOnIsInPipStateChangedListener.accept(true /* inPip */); + for (Consumer<Boolean> listener : mOnIsInPipStateChangedListeners) { + listener.accept(true /* inPip */); } break; case PipTransitionState.EXITED_PIP: - if (mOnIsInPipStateChangedListener != null) { - mOnIsInPipStateChangedListener.accept(false /* inPip */); + for (Consumer<Boolean> listener : mOnIsInPipStateChangedListeners) { + listener.accept(false /* inPip */); } break; } @@ -451,13 +454,19 @@ public class PipController implements ConfigurationChangeListener, mPipTransitionState.dump(pw, innerPrefix); } - private void setOnIsInPipStateChangedListener(Consumer<Boolean> callback) { - mOnIsInPipStateChangedListener = callback; - if (mOnIsInPipStateChangedListener != null) { + private void addOnIsInPipStateChangedListener(@NonNull Consumer<Boolean> callback) { + if (callback != null) { + mOnIsInPipStateChangedListeners.add(callback); callback.accept(mPipTransitionState.isInPip()); } } + private void removeOnIsInPipStateChangedListener(@NonNull Consumer<Boolean> callback) { + if (callback != null) { + mOnIsInPipStateChangedListeners.remove(callback); + } + } + private void setLauncherAppIconSize(int iconSizePx) { mPipBoundsState.getLauncherState().setAppIconSizePx(iconSizePx); } @@ -473,9 +482,16 @@ public class PipController implements ConfigurationChangeListener, public void onSystemUiStateChanged(boolean isSysUiStateValid, long flag) {} @Override - public void setOnIsInPipStateChangedListener(Consumer<Boolean> callback) { + public void addOnIsInPipStateChangedListener(@NonNull Consumer<Boolean> callback) { + mMainExecutor.execute(() -> { + PipController.this.addOnIsInPipStateChangedListener(callback); + }); + } + + @Override + public void removeOnIsInPipStateChangedListener(@NonNull Consumer<Boolean> callback) { mMainExecutor.execute(() -> { - PipController.this.setOnIsInPipStateChangedListener(callback); + PipController.this.removeOnIsInPipStateChangedListener(callback); }); } 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 607de0eccd77..5438a014af00 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 @@ -25,10 +25,13 @@ import android.content.Context; import android.graphics.Matrix; import android.graphics.Rect; import android.view.SurfaceControl; +import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.ProtoLog; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.pip.PipBoundsState; @@ -48,11 +51,13 @@ public class PipScheduler { private final ShellExecutor mMainExecutor; private final PipTransitionState mPipTransitionState; private PipTransitionController mPipTransitionController; - private final PipSurfaceTransactionHelper.SurfaceControlTransactionFactory + private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory mSurfaceControlTransactionFactory; @Nullable private Runnable mUpdateMovementBoundsRunnable; + private PipAlphaAnimatorSupplier mPipAlphaAnimatorSupplier; + public PipScheduler(Context context, PipBoundsState pipBoundsState, ShellExecutor mainExecutor, @@ -64,10 +69,7 @@ public class PipScheduler { mSurfaceControlTransactionFactory = new PipSurfaceTransactionHelper.VsyncSurfaceControlTransactionFactory(); - } - - ShellExecutor getMainExecutor() { - return mMainExecutor; + mPipAlphaAnimatorSupplier = PipAlphaAnimator::new; } void setPipTransitionController(PipTransitionController pipTransitionController) { @@ -76,27 +78,29 @@ public class PipScheduler { @Nullable private WindowContainerTransaction getExitPipViaExpandTransaction() { - if (mPipTransitionState.mPipTaskToken == null) { + WindowContainerToken pipTaskToken = mPipTransitionState.getPipTaskToken(); + if (pipTaskToken == null) { return null; } WindowContainerTransaction wct = new WindowContainerTransaction(); // final expanded bounds to be inherited from the parent - wct.setBounds(mPipTransitionState.mPipTaskToken, null); + wct.setBounds(pipTaskToken, null); // if we are hitting a multi-activity case // windowing mode change will reparent to original host task - wct.setWindowingMode(mPipTransitionState.mPipTaskToken, WINDOWING_MODE_UNDEFINED); + wct.setWindowingMode(pipTaskToken, WINDOWING_MODE_UNDEFINED); return wct; } @Nullable private WindowContainerTransaction getRemovePipTransaction() { - if (mPipTransitionState.mPipTaskToken == null) { + WindowContainerToken pipTaskToken = mPipTransitionState.getPipTaskToken(); + if (pipTaskToken == null) { return null; } WindowContainerTransaction wct = new WindowContainerTransaction(); - wct.setBounds(mPipTransitionState.mPipTaskToken, null); - wct.setWindowingMode(mPipTransitionState.mPipTaskToken, WINDOWING_MODE_UNDEFINED); - wct.reorder(mPipTransitionState.mPipTaskToken, false); + wct.setBounds(pipTaskToken, null); + wct.setWindowingMode(pipTaskToken, WINDOWING_MODE_UNDEFINED); + wct.reorder(pipTaskToken, false); return wct; } @@ -117,7 +121,7 @@ public class PipScheduler { /** Runs remove PiP animation and schedules remove PiP transition after the animation ends. */ public void removePipAfterAnimation() { SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction(); - PipAlphaAnimator animator = new PipAlphaAnimator(mContext, + PipAlphaAnimator animator = mPipAlphaAnimatorSupplier.get(mContext, mPipTransitionState.getPinnedTaskLeash(), tx, PipAlphaAnimator.FADE_OUT); animator.setAnimationEndCallback(this::scheduleRemovePipImmediately); animator.start(); @@ -159,13 +163,14 @@ public class PipScheduler { * for running the animator will get this as an extra. */ public void scheduleAnimateResizePip(Rect toBounds, boolean configAtEnd, int duration) { - if (mPipTransitionState.mPipTaskToken == null || !mPipTransitionState.isInPip()) { + WindowContainerToken pipTaskToken = mPipTransitionState.getPipTaskToken(); + if (pipTaskToken == null || !mPipTransitionState.isInPip()) { return; } WindowContainerTransaction wct = new WindowContainerTransaction(); - wct.setBounds(mPipTransitionState.mPipTaskToken, toBounds); + wct.setBounds(pipTaskToken, toBounds); if (configAtEnd) { - wct.deferConfigToTransitionEnd(mPipTransitionState.mPipTaskToken); + wct.deferConfigToTransitionEnd(pipTaskToken); } mPipTransitionController.startResizeTransition(wct, duration); } @@ -204,7 +209,7 @@ public class PipScheduler { return; } SurfaceControl leash = mPipTransitionState.getPinnedTaskLeash(); - final SurfaceControl.Transaction tx = new SurfaceControl.Transaction(); + final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction(); Matrix transformTensor = new Matrix(); final float[] mMatrixTmp = new float[9]; @@ -218,7 +223,7 @@ public class PipScheduler { tx.apply(); } - void setUpdateMovementBoundsRunnable(Runnable updateMovementBoundsRunnable) { + void setUpdateMovementBoundsRunnable(@Nullable Runnable updateMovementBoundsRunnable) { mUpdateMovementBoundsRunnable = updateMovementBoundsRunnable; } @@ -235,4 +240,23 @@ public class PipScheduler { mPipBoundsState.setBounds(newBounds); maybeUpdateMovementBounds(); } + + @VisibleForTesting + void setSurfaceControlTransactionFactory( + @NonNull PipSurfaceTransactionHelper.SurfaceControlTransactionFactory factory) { + mSurfaceControlTransactionFactory = factory; + } + + @VisibleForTesting + interface PipAlphaAnimatorSupplier { + PipAlphaAnimator get(@NonNull Context context, + SurfaceControl leash, + SurfaceControl.Transaction tx, + @PipAlphaAnimator.Fade int direction); + } + + @VisibleForTesting + void setPipAlphaAnimatorSupplier(@NonNull PipAlphaAnimatorSupplier supplier) { + mPipAlphaAnimatorSupplier = supplier; + } } 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 ea783e9cadb6..02f595537d03 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 @@ -20,6 +20,7 @@ import static android.app.WindowConfiguration.ROTATION_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.view.Surface.ROTATION_0; import static android.view.Surface.ROTATION_270; +import static android.view.Surface.ROTATION_90; import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_PIP; @@ -230,6 +231,11 @@ public class PipTransition extends PipTransitionController implements // If there is no PiP change, exit this transition handler and potentially try others. if (pipChange == null) return false; + // Other targets might have default transforms applied that are not relevant when + // playing PiP transitions, so reset those transforms if needed. + prepareOtherTargetTransforms(info, startTransaction, finishTransaction); + + // 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()); @@ -341,17 +347,21 @@ public class PipTransition extends PipTransitionController implements (destinationBounds.height() - overlaySize) / 2f); } - final int startRotation = pipChange.getStartRotation(); - final int endRotation = mPipDisplayLayoutState.getRotation(); - final int delta = endRotation == ROTATION_UNDEFINED ? ROTATION_0 - : startRotation - endRotation; + final int delta = getFixedRotationDelta(info, pipChange); if (delta != ROTATION_0) { - mPipTransitionState.setInFixedRotation(true); - handleBoundsEnterFixedRotation(pipChange, pipActivityChange, endRotation); + // Update transition target changes in place to prepare for fixed rotation. + handleBoundsEnterFixedRotation(info, pipChange, pipActivityChange); } + // Update the src-rect-hint in params in place, to set up initial animator transform. + Rect sourceRectHint = getAdjustedSourceRectHint(info, pipChange, pipActivityChange); + pipChange.getTaskInfo().pictureInPictureParams.getSourceRectHint().set(sourceRectHint); + + // Config-at-end transitions need to have their activities transformed before starting + // the animation; this makes the buffer seem like it's been updated to final size. prepareConfigAtEndActivity(startTransaction, finishTransaction, pipChange, pipActivityChange); + startTransaction.merge(finishTransaction); PipEnterAnimator animator = new PipEnterAnimator(mContext, pipLeash, startTransaction, finishTransaction, destinationBounds, delta); @@ -387,55 +397,36 @@ public class PipTransition extends PipTransitionController implements return false; } + final SurfaceControl pipLeash = getLeash(pipChange); final Rect startBounds = pipChange.getStartAbsBounds(); final Rect endBounds = pipChange.getEndAbsBounds(); - final PictureInPictureParams params = pipChange.getTaskInfo().pictureInPictureParams; - final float aspectRatio = mPipBoundsAlgorithm.getAspectRatioOrDefault(params); - final Rect sourceRectHint = PipBoundsAlgorithm.getValidSourceHintRect(params, startBounds, - endBounds); - final Rect adjustedSourceRectHint = sourceRectHint != null ? new Rect(sourceRectHint) - : PipUtils.getEnterPipWithOverlaySrcRectHint(startBounds, aspectRatio); - - final SurfaceControl pipLeash = mPipTransitionState.getPinnedTaskLeash(); - - // For opening type transitions, if there is a change of mode TO_FRONT/OPEN, - // make sure that change has alpha of 1f, since it's init state might be set to alpha=0f - // by the Transitions framework to simplify Task opening transitions. - if (TransitionUtil.isOpeningType(info.getType())) { - for (TransitionInfo.Change change : info.getChanges()) { - if (change.getLeash() == null) continue; - if (change.getMode() == TRANSIT_OPEN || change.getMode() == TRANSIT_TO_FRONT) { - startTransaction.setAlpha(change.getLeash(), 1f); - } - } - } - - final TransitionInfo.Change fixedRotationChange = findFixedRotationChange(info); - final int startRotation = pipChange.getStartRotation(); - final int endRotation = fixedRotationChange != null - ? fixedRotationChange.getEndFixedRotation() : ROTATION_UNDEFINED; - final int delta = endRotation == ROTATION_UNDEFINED ? ROTATION_0 - : startRotation - endRotation; + final Rect adjustedSourceRectHint = getAdjustedSourceRectHint(info, pipChange, + pipActivityChange); + final int delta = getFixedRotationDelta(info, pipChange); if (delta != ROTATION_0) { - mPipTransitionState.setInFixedRotation(true); - handleBoundsEnterFixedRotation(pipChange, pipActivityChange, - fixedRotationChange.getEndFixedRotation()); + // Update transition target changes in place to prepare for fixed rotation. + handleBoundsEnterFixedRotation(info, pipChange, pipActivityChange); } PipEnterAnimator animator = new PipEnterAnimator(mContext, pipLeash, startTransaction, finishTransaction, endBounds, delta); - if (sourceRectHint == null) { - // update the src-rect-hint in params in place, to set up initial animator transform. - params.getSourceRectHint().set(adjustedSourceRectHint); + if (PipBoundsAlgorithm.getValidSourceHintRect(params, startBounds, endBounds) == null) { + // If app provided src-rect-hint is invalid, use app icon overlay. animator.setAppIconContentOverlay( mContext, startBounds, endBounds, pipChange.getTaskInfo().topActivityInfo, mPipBoundsState.getLauncherState().getAppIconSizePx()); } + // Update the src-rect-hint in params in place, to set up initial animator transform. + params.getSourceRectHint().set(adjustedSourceRectHint); + + // Config-at-end transitions need to have their activities transformed before starting + // the animation; this makes the buffer seem like it's been updated to final size. prepareConfigAtEndActivity(startTransaction, finishTransaction, pipChange, pipActivityChange); + animator.setAnimationStartCallback(() -> animator.setEnterStartState(pipChange)); animator.setAnimationEndCallback(() -> { if (animator.getContentOverlayLeash() != null) { @@ -457,11 +448,22 @@ public class PipTransition extends PipTransitionController implements animator.start(); } - private void handleBoundsEnterFixedRotation(TransitionInfo.Change pipTaskChange, - TransitionInfo.Change pipActivityChange, int endRotation) { - final Rect endBounds = pipTaskChange.getEndAbsBounds(); - final Rect endActivityBounds = pipActivityChange.getEndAbsBounds(); - int startRotation = pipTaskChange.getStartRotation(); + private void handleBoundsEnterFixedRotation(TransitionInfo info, + TransitionInfo.Change outPipTaskChange, + TransitionInfo.Change outPipActivityChange) { + final TransitionInfo.Change fixedRotationChange = findFixedRotationChange(info); + final Rect endBounds = outPipTaskChange.getEndAbsBounds(); + final Rect endActivityBounds = outPipActivityChange.getEndAbsBounds(); + int startRotation = outPipTaskChange.getStartRotation(); + int endRotation = fixedRotationChange != null + ? fixedRotationChange.getEndFixedRotation() : mPipDisplayLayoutState.getRotation(); + + if (startRotation == endRotation) { + return; + } + + // This is used by display change listeners to respond properly to fixed rotation. + mPipTransitionState.setInFixedRotation(true); // Cache the task to activity offset to potentially restore later. Point activityEndOffset = new Point(endActivityBounds.left - endBounds.left, @@ -490,15 +492,15 @@ public class PipTransition extends PipTransitionController implements endBounds.top + activityEndOffset.y); } - private void handleExpandFixedRotation(TransitionInfo.Change pipTaskChange, int endRotation) { - final Rect endBounds = pipTaskChange.getEndAbsBounds(); + private void handleExpandFixedRotation(TransitionInfo.Change outPipTaskChange, int delta) { + final Rect endBounds = outPipTaskChange.getEndAbsBounds(); final int width = endBounds.width(); final int height = endBounds.height(); final int left = endBounds.left; final int top = endBounds.top; int newTop, newLeft; - if (endRotation == Surface.ROTATION_90) { + if (delta == Surface.ROTATION_90) { newLeft = top; newTop = -(left + width); } else { @@ -541,7 +543,7 @@ public class PipTransition extends PipTransitionController implements @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, @NonNull Transitions.TransitionFinishCallback finishCallback) { - WindowContainerToken pipToken = mPipTransitionState.mPipTaskToken; + WindowContainerToken pipToken = mPipTransitionState.getPipTaskToken(); TransitionInfo.Change pipChange = getChangeByToken(info, pipToken); if (pipChange == null) { @@ -585,15 +587,11 @@ public class PipTransition extends PipTransitionController implements final Rect sourceRectHint = PipBoundsAlgorithm.getValidSourceHintRect(params, endBounds, startBounds); - final TransitionInfo.Change fixedRotationChange = findFixedRotationChange(info); - final int startRotation = pipChange.getStartRotation(); - final int endRotation = fixedRotationChange != null - ? fixedRotationChange.getEndFixedRotation() : ROTATION_UNDEFINED; - final int delta = endRotation == ROTATION_UNDEFINED ? ROTATION_0 - : endRotation - startRotation; - + // We define delta = startRotation - endRotation, so we need to flip the sign. + final int delta = -getFixedRotationDelta(info, pipChange); if (delta != ROTATION_0) { - handleExpandFixedRotation(pipChange, endRotation); + // Update PiP target change in place to prepare for fixed rotation; + handleExpandFixedRotation(pipChange, delta); } PipExpandAnimator animator = new PipExpandAnimator(mContext, pipLeash, @@ -661,6 +659,72 @@ public class PipTransition extends PipTransitionController implements return null; } + @NonNull + private Rect getAdjustedSourceRectHint(@NonNull TransitionInfo info, + @NonNull TransitionInfo.Change pipTaskChange, + @NonNull TransitionInfo.Change pipActivityChange) { + final Rect startBounds = pipTaskChange.getStartAbsBounds(); + final Rect endBounds = pipTaskChange.getEndAbsBounds(); + final PictureInPictureParams params = pipTaskChange.getTaskInfo().pictureInPictureParams; + + // Get the source-rect-hint provided by the app and check its validity; null if invalid. + final Rect sourceRectHint = PipBoundsAlgorithm.getValidSourceHintRect(params, startBounds, + endBounds); + + final Rect adjustedSourceRectHint = new Rect(); + if (sourceRectHint != null) { + adjustedSourceRectHint.set(sourceRectHint); + // If multi-activity PiP, use the parent task before PiP to retrieve display cutouts; + // then, offset the valid app provided source rect hint by the cutout insets. + // For single-activity PiP, just use the pinned task to get the cutouts instead. + TransitionInfo.Change parentBeforePip = pipActivityChange.getLastParent() != null + ? getChangeByToken(info, pipActivityChange.getLastParent()) : null; + Rect cutoutInsets = parentBeforePip != null + ? parentBeforePip.getTaskInfo().displayCutoutInsets + : pipTaskChange.getTaskInfo().displayCutoutInsets; + if (cutoutInsets != null + && getFixedRotationDelta(info, pipTaskChange) == ROTATION_90) { + adjustedSourceRectHint.offset(cutoutInsets.left, cutoutInsets.top); + } + } else { + // For non-valid app provided src-rect-hint, calculate one to crop into during + // app icon overlay animation. + float aspectRatio = mPipBoundsAlgorithm.getAspectRatioOrDefault(params); + adjustedSourceRectHint.set( + PipUtils.getEnterPipWithOverlaySrcRectHint(startBounds, aspectRatio)); + } + return adjustedSourceRectHint; + } + + @Surface.Rotation + private int getFixedRotationDelta(@NonNull TransitionInfo info, + @NonNull TransitionInfo.Change pipChange) { + TransitionInfo.Change fixedRotationChange = findFixedRotationChange(info); + int startRotation = pipChange.getStartRotation(); + int endRotation = fixedRotationChange != null + ? fixedRotationChange.getEndFixedRotation() : mPipDisplayLayoutState.getRotation(); + int delta = endRotation == ROTATION_UNDEFINED ? ROTATION_0 + : startRotation - endRotation; + return delta; + } + + private void prepareOtherTargetTransforms(TransitionInfo info, + SurfaceControl.Transaction startTransaction, + SurfaceControl.Transaction finishTransaction) { + // For opening type transitions, if there is a change of mode TO_FRONT/OPEN, + // make sure that change has alpha of 1f, since it's init state might be set to alpha=0f + // by the Transitions framework to simplify Task opening transitions. + if (TransitionUtil.isOpeningType(info.getType())) { + for (TransitionInfo.Change change : info.getChanges()) { + if (change.getLeash() == null) continue; + if (change.getMode() == TRANSIT_OPEN || change.getMode() == TRANSIT_TO_FRONT) { + startTransaction.setAlpha(change.getLeash(), 1f); + } + } + } + + } + private WindowContainerTransaction getEnterPipTransaction(@NonNull IBinder transition, @NonNull TransitionRequestInfo request) { // cache the original task token to check for multi-activity case later @@ -709,11 +773,11 @@ public class PipTransition extends PipTransitionController implements } private boolean isRemovePipTransition(@NonNull TransitionInfo info) { - if (mPipTransitionState.mPipTaskToken == null) { + if (mPipTransitionState.getPipTaskToken() == null) { // PiP removal makes sense if enter-PiP has cached a valid pinned task token. return false; } - TransitionInfo.Change pipChange = info.getChange(mPipTransitionState.mPipTaskToken); + TransitionInfo.Change pipChange = info.getChange(mPipTransitionState.getPipTaskToken()); if (pipChange == null) { // Search for the PiP change by token since the windowing mode might be FULLSCREEN now. return false; @@ -795,18 +859,18 @@ public class PipTransition extends PipTransitionController implements Preconditions.checkState(extra != null, "No extra bundle for " + mPipTransitionState); - mPipTransitionState.mPipTaskToken = extra.getParcelable( - PIP_TASK_TOKEN, WindowContainerToken.class); + mPipTransitionState.setPipTaskToken(extra.getParcelable( + PIP_TASK_TOKEN, WindowContainerToken.class)); mPipTransitionState.setPinnedTaskLeash(extra.getParcelable( PIP_TASK_LEASH, SurfaceControl.class)); - boolean hasValidTokenAndLeash = mPipTransitionState.mPipTaskToken != null + boolean hasValidTokenAndLeash = mPipTransitionState.getPipTaskToken() != null && mPipTransitionState.getPinnedTaskLeash() != null; Preconditions.checkState(hasValidTokenAndLeash, "Unexpected bundle for " + mPipTransitionState); break; case PipTransitionState.EXITED_PIP: - mPipTransitionState.mPipTaskToken = null; + mPipTransitionState.setPipTaskToken(null); mPipTransitionState.setPinnedTaskLeash(null); break; } 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 03e06f906015..8e90bfee2636 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 @@ -138,7 +138,7 @@ public class PipTransitionState { // pinned PiP task's WC token @Nullable - WindowContainerToken mPipTaskToken; + private WindowContainerToken mPipTaskToken; // pinned PiP task's leash @Nullable @@ -304,6 +304,14 @@ public class PipTransitionState { mSwipePipToHomeAppBounds.setEmpty(); } + @Nullable WindowContainerToken getPipTaskToken() { + return mPipTaskToken; + } + + public void setPipTaskToken(@Nullable WindowContainerToken token) { + mPipTaskToken = token; + } + @Nullable SurfaceControl getPinnedTaskLeash() { return mPinnedTaskLeash; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java index f739d65e63c3..49cf8ae81aa8 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java @@ -63,6 +63,8 @@ public enum ShellProtoLogGroup implements IProtoLogGroup { "Bubbles"), WM_SHELL_COMPAT_UI(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false, Consts.TAG_WM_COMPAT_UI), + WM_SHELL_APP_COMPAT(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false, + Consts.TAG_WM_APP_COMPAT), TEST_GROUP(true, true, false, "WindowManagerShellProtoLogTest"); private final boolean mEnabled; @@ -131,6 +133,7 @@ public enum ShellProtoLogGroup implements IProtoLogGroup { private static final String TAG_WM_SPLIT_SCREEN = "ShellSplitScreen"; private static final String TAG_WM_DESKTOP_MODE = "ShellDesktopMode"; private static final String TAG_WM_COMPAT_UI = "CompatUi"; + private static final String TAG_WM_APP_COMPAT = "AppCompat"; private static final boolean ENABLE_DEBUG = true; private static final boolean ENABLE_LOG_TO_PROTO_DEBUG = true; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java index 40065b9287a6..417a6558ffcc 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java @@ -34,6 +34,8 @@ import static android.window.TransitionInfo.FLAG_TRANSLUCENT; import static com.android.wm.shell.shared.ShellSharedConstants.KEY_EXTRA_SHELL_CAN_HAND_OFF_ANIMATION; import static com.android.wm.shell.shared.split.SplitBounds.KEY_EXTRA_SPLIT_BOUNDS; +import static com.android.wm.shell.transition.Transitions.TRANSIT_END_RECENTS_TRANSITION; +import static com.android.wm.shell.transition.Transitions.TRANSIT_START_RECENTS_TRANSITION; import android.annotation.Nullable; import android.annotation.SuppressLint; @@ -67,6 +69,7 @@ import androidx.annotation.NonNull; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.IResultReceiver; import com.android.internal.protolog.ProtoLog; +import com.android.wm.shell.Flags; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.pip.PipUtils; @@ -216,8 +219,11 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler, break; } } - final IBinder transition = mTransitions.startTransition(TRANSIT_TO_FRONT, wct, - mixer == null ? this : mixer); + final int transitionType = Flags.enableShellTopTaskTracking() + ? TRANSIT_START_RECENTS_TRANSITION + : TRANSIT_TO_FRONT; + final IBinder transition = mTransitions.startTransition(transitionType, + wct, mixer == null ? this : mixer); if (mixer != null) { setTransitionForMixer.accept(transition); } @@ -300,7 +306,7 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler, "RecentsTransitionHandler.mergeAnimation: no controller found"); return; } - controller.merge(info, t, finishCallback); + controller.merge(info, t, mergeTarget, finishCallback); } @Override @@ -367,6 +373,8 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler, private boolean mPausingSeparateHome = false; private ArrayMap<SurfaceControl, SurfaceControl> mLeashMap = null; private PictureInPictureSurfaceTransaction mPipTransaction = null; + // This is the transition that backs the entire recents transition, and the one that the + // pending finish transition below will be merged into private IBinder mTransition = null; private boolean mKeyguardLocked = false; private boolean mWillFinishToHome = false; @@ -386,6 +394,10 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler, // next called. private Pair<int[], TaskSnapshot[]> mPendingPauseSnapshotsForCancel; + // Used to track a pending finish transition + private IBinder mPendingFinishTransition; + private IResultReceiver mPendingRunnerFinishCb; + RecentsController(IRecentsAnimationRunner listener) { mInstanceId = System.identityHashCode(this); mListener = listener; @@ -523,6 +535,11 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler, mInfo = null; mTransition = null; mPendingPauseSnapshotsForCancel = null; + mPipTaskId = -1; + mPipTask = null; + mPipTransaction = null; + mPendingRunnerFinishCb = null; + mPendingFinishTransition = null; mControllers.remove(this); for (int i = 0; i < mStateListeners.size(); i++) { mStateListeners.get(i).onAnimationStateChanged(false); @@ -734,6 +751,8 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler, // the pausing apps. t.setLayer(target.leash, layer); } else if (taskInfo != null && taskInfo.topActivityType == ACTIVITY_TYPE_HOME) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, + " not handling home taskId=%d", taskInfo.taskId); // do nothing } else if (TransitionUtil.isOpeningType(change.getMode())) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, @@ -872,16 +891,35 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler, } } + /** + * Note: because we use a book-end transition to finish the recents transition, we must + * either always merge the incoming transition, or always cancel the recents transition + * if we don't handle the incoming transition to ensure that the end transition is queued + * before any unhandled transitions. + */ @SuppressLint("NewApi") - void merge(TransitionInfo info, SurfaceControl.Transaction t, + void merge(TransitionInfo info, SurfaceControl.Transaction t, IBinder mergeTarget, Transitions.TransitionFinishCallback finishCallback) { if (mFinishCB == null) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, "[%d] RecentsController.merge: skip, no finish callback", mInstanceId); - // This was no-op'd (likely a repeated start) and we've already sent finish. + // This was no-op'd (likely a repeated start) and we've already completed finish. return; } + + if (Flags.enableShellTopTaskTracking() + && info.getType() == TRANSIT_END_RECENTS_TRANSITION + && mergeTarget == mTransition) { + // This is a pending finish, so merge the end transition to trigger completing the + // cleanup of the recents transition + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, + "[%d] RecentsController.merge: TRANSIT_END_RECENTS_TRANSITION", + mInstanceId); + finishCallback.onTransitionFinished(null /* wct */); + return; + } + if (info.getType() == TRANSIT_SLEEP) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, "[%d] RecentsController.merge: transit_sleep", mInstanceId); @@ -1245,8 +1283,19 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler, return; } - if (mFinishCB == null) { + if (mFinishCB == null + || (Flags.enableShellTopTaskTracking() && mPendingFinishTransition != null)) { Slog.e(TAG, "Duplicate call to finish"); + if (runnerFinishCb != null) { + try { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, + "[%d] RecentsController.finishInner: calling finish callback", + mInstanceId); + runnerFinishCb.send(0, null); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to report transition finished", e); + } + } return; } @@ -1254,19 +1303,22 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler, && !mWillFinishToHome && mPausingTasks != null && mState == STATE_NORMAL; - if (returningToApp && allAppsAreTranslucent(mPausingTasks)) { - mHomeTransitionObserver.notifyHomeVisibilityChanged(true); - } else if (!toHome) { - // For some transitions, we may have notified home activity that it became visible. - // We need to notify the observer that we are no longer going home. - mHomeTransitionObserver.notifyHomeVisibilityChanged(false); + if (!Flags.enableShellTopTaskTracking()) { + // This is only necessary when the recents transition is finished using a finishWCT, + // otherwise a new transition will notify the relevant observers + if (returningToApp && allAppsAreTranslucent(mPausingTasks)) { + mHomeTransitionObserver.notifyHomeVisibilityChanged(true); + } else if (!toHome) { + // For some transitions, we may have notified home activity that it became + // visible. We need to notify the observer that we are no longer going home. + mHomeTransitionObserver.notifyHomeVisibilityChanged(false); + } } + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, "[%d] RecentsController.finishInner: toHome=%b userLeave=%b " - + "willFinishToHome=%b state=%d", - mInstanceId, toHome, sendUserLeaveHint, mWillFinishToHome, mState); - final Transitions.TransitionFinishCallback finishCB = mFinishCB; - mFinishCB = null; + + "willFinishToHome=%b state=%d reason=%s", + mInstanceId, toHome, sendUserLeaveHint, mWillFinishToHome, mState, reason); final SurfaceControl.Transaction t = mFinishTransaction; final WindowContainerTransaction wct = new WindowContainerTransaction(); @@ -1328,6 +1380,7 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler, for (int i = 0; i < mClosingTasks.size(); ++i) { cleanUpPausingOrClosingTask(mClosingTasks.get(i), wct, t, sendUserLeaveHint); } + if (mPipTransaction != null && sendUserLeaveHint) { SurfaceControl pipLeash = null; TransitionInfo.Change pipChange = null; @@ -1379,15 +1432,50 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler, mTransitions.startTransition(TRANSIT_PIP, wct, null /* handler */); // We need to clear the WCT to send finishWCT=null for Recents. wct.clear(); + + if (Flags.enableShellTopTaskTracking()) { + // In this case, we've already started the PIP transition, so we can + // clean up immediately + mPendingRunnerFinishCb = runnerFinishCb; + onFinishInner(null); + return; + } } } - mPipTaskId = -1; - mPipTask = null; - mPipTransaction = null; } } + + if (Flags.enableShellTopTaskTracking()) { + if (!wct.isEmpty()) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, + "[%d] RecentsController.finishInner: " + + "Queuing TRANSIT_END_RECENTS_TRANSITION", mInstanceId); + mPendingRunnerFinishCb = runnerFinishCb; + mPendingFinishTransition = mTransitions.startTransition( + TRANSIT_END_RECENTS_TRANSITION, wct, + new PendingFinishTransitionHandler()); + } else { + // If there's no work to do, just go ahead and clean up + mPendingRunnerFinishCb = runnerFinishCb; + onFinishInner(null /* wct */); + } + } else { + mPendingRunnerFinishCb = runnerFinishCb; + onFinishInner(wct); + } + } + + /** + * Runs the actual logic to finish the recents transition. + */ + private void onFinishInner(@Nullable WindowContainerTransaction wct) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, + "[%d] RecentsController.finishInner: Completing finish", mInstanceId); + final Transitions.TransitionFinishCallback finishCb = mFinishCB; + final IResultReceiver runnerFinishCb = mPendingRunnerFinishCb; + cleanUp(); - finishCB.onTransitionFinished(wct.isEmpty() ? null : wct); + finishCb.onTransitionFinished(wct); if (runnerFinishCb != null) { try { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, @@ -1472,6 +1560,40 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler, } }); } + + /** + * A temporary transition handler used with the pending finish transition, which runs the + * cleanup/finish logic once the pending transition is merged/handled. + * This is only initialized if Flags.enableShellTopTaskTracking() is enabled. + */ + private class PendingFinishTransitionHandler implements Transitions.TransitionHandler { + @Override + public boolean startAnimation(@NonNull IBinder transition, + @NonNull TransitionInfo info, + @NonNull SurfaceControl.Transaction startTransaction, + @NonNull SurfaceControl.Transaction finishTransaction, + @NonNull Transitions.TransitionFinishCallback finishCallback) { + return false; + } + + @Nullable + @Override + public WindowContainerTransaction handleRequest(@NonNull IBinder transition, + @NonNull TransitionRequestInfo request) { + return null; + } + + @Override + public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted, + @Nullable SurfaceControl.Transaction finishTransaction) { + // Once we have merged (or not if the WCT didn't result in any changes), then we can + // run the pending finish logic + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, + "[%d] RecentsController.onTransitionConsumed: " + + "Consumed pending finish transition", mInstanceId); + onFinishInner(null /* wct */); + } + }; }; /** Utility class to track the state of a task as-seen by recents. */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java index 29e4b5bca5cc..9fcf98b9efc2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java @@ -353,7 +353,7 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { boolean isSeamlessDisplayChange = false; if (mode == TRANSIT_CHANGE && change.hasFlags(FLAG_IS_DISPLAY)) { - if (info.getType() == TRANSIT_CHANGE) { + if (info.getType() == TRANSIT_CHANGE || isOnlyTranslucent) { final int anim = getRotationAnimationHint(change, info, mDisplayController); isSeamlessDisplayChange = anim == ROTATION_ANIMATION_SEAMLESS; if (!(isSeamlessDisplayChange || anim == ROTATION_ANIMATION_JUMPCUT)) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java index 1d456aed5f4d..3f191497e1ed 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java @@ -203,6 +203,12 @@ public class Transitions implements RemoteCallable<Transitions>, /** Transition type to minimize a task. */ public static final int TRANSIT_MINIMIZE = WindowManager.TRANSIT_FIRST_CUSTOM + 20; + /** Transition to start the recents transition */ + public static final int TRANSIT_START_RECENTS_TRANSITION = TRANSIT_FIRST_CUSTOM + 21; + + /** Transition to end the recents transition */ + public static final int TRANSIT_END_RECENTS_TRANSITION = TRANSIT_FIRST_CUSTOM + 22; + /** Transition type for desktop mode transitions. */ public static final int TRANSIT_DESKTOP_MODE_TYPES = WindowManager.TRANSIT_FIRST_CUSTOM + 100; @@ -1875,6 +1881,8 @@ public class Transitions implements RemoteCallable<Transitions>, case TRANSIT_SPLIT_PASSTHROUGH -> "SPLIT_PASSTHROUGH"; case TRANSIT_CLEANUP_PIP_EXIT -> "CLEANUP_PIP_EXIT"; case TRANSIT_MINIMIZE -> "MINIMIZE"; + case TRANSIT_START_RECENTS_TRANSITION -> "START_RECENTS_TRANSITION"; + case TRANSIT_END_RECENTS_TRANSITION -> "END_RECENTS_TRANSITION"; default -> ""; }; return typeStr + "(FIRST_CUSTOM+" + (transitType - TRANSIT_FIRST_CUSTOM) + ")"; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java index 7265fb8f8027..c9f2d2e8c0e2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java @@ -26,6 +26,7 @@ import static android.view.WindowManager.TRANSIT_CHANGE; import static com.android.window.flags.Flags.enableDisplayFocusInShellTransitions; +import android.annotation.NonNull; import android.app.ActivityManager.RunningTaskInfo; import android.content.ContentResolver; import android.content.Context; @@ -110,6 +111,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel, FocusT } mMainExecutor.execute(() -> { mExclusionRegion.set(systemGestureExclusion); + onExclusionRegionChanged(displayId, mExclusionRegion); }); } }; @@ -163,7 +165,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel, FocusT boolean isFocusedGlobally) { final WindowDecoration decor = mWindowDecorByTaskId.get(taskId); if (decor != null) { - decor.relayout(decor.mTaskInfo, isFocusedGlobally); + decor.relayout(decor.mTaskInfo, isFocusedGlobally, decor.mExclusionRegion); } } @@ -199,9 +201,9 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel, FocusT if (enableDisplayFocusInShellTransitions()) { // Pass the current global focus status to avoid updates outside of a ShellTransition. - decoration.relayout(taskInfo, decoration.mHasGlobalFocus); + decoration.relayout(taskInfo, decoration.mHasGlobalFocus, decoration.mExclusionRegion); } else { - decoration.relayout(taskInfo, taskInfo.isFocused); + decoration.relayout(taskInfo, taskInfo.isFocused, decoration.mExclusionRegion); } } @@ -240,7 +242,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel, FocusT } else { decoration.relayout(taskInfo, startT, finishT, false /* applyStartTransactionOnDraw */, false /* setTaskCropAndPosition */, - mFocusTransitionObserver.hasGlobalFocus(taskInfo)); + mFocusTransitionObserver.hasGlobalFocus(taskInfo), mExclusionRegion); } } @@ -254,7 +256,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel, FocusT decoration.relayout(taskInfo, startT, finishT, false /* applyStartTransactionOnDraw */, false /* setTaskCropAndPosition */, - mFocusTransitionObserver.hasGlobalFocus(taskInfo)); + mFocusTransitionObserver.hasGlobalFocus(taskInfo), mExclusionRegion); } @Override @@ -266,6 +268,15 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel, FocusT decoration.close(); } + private void onExclusionRegionChanged(int displayId, @NonNull Region exclusionRegion) { + final int decorCount = mWindowDecorByTaskId.size(); + for (int i = 0; i < decorCount; i++) { + final CaptionWindowDecoration decoration = mWindowDecorByTaskId.valueAt(i); + if (decoration.mTaskInfo.displayId != displayId) continue; + decoration.onExclusionRegionChanged(exclusionRegion); + } + } + private boolean shouldShowWindowDecor(RunningTaskInfo taskInfo) { if (taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) { return true; @@ -333,7 +344,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel, FocusT windowDecoration.setTaskDragResizer(taskPositioner); windowDecoration.relayout(taskInfo, startT, finishT, false /* applyStartTransactionOnDraw */, false /* setTaskCropAndPosition */, - mFocusTransitionObserver.hasGlobalFocus(taskInfo)); + mFocusTransitionObserver.hasGlobalFocus(taskInfo), mExclusionRegion); } private class CaptionTouchEventListener implements 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 c9546731a193..982fda0ddf36 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 @@ -36,6 +36,7 @@ import android.graphics.Color; import android.graphics.Insets; import android.graphics.Point; import android.graphics.Rect; +import android.graphics.Region; import android.graphics.drawable.GradientDrawable; import android.os.Handler; import android.util.Size; @@ -174,7 +175,8 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL } @Override - void relayout(RunningTaskInfo taskInfo, boolean hasGlobalFocus) { + void relayout(RunningTaskInfo taskInfo, boolean hasGlobalFocus, + @NonNull Region displayExclusionRegion) { final SurfaceControl.Transaction t = new SurfaceControl.Transaction(); // The crop and position of the task should only be set when a task is fluid resizing. In // all other cases, it is expected that the transition handler positions and crops the task @@ -186,7 +188,7 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL // synced with the buffer transaction (that draws the View). Both will be shown on screen // at the same, whereas applying them independently causes flickering. See b/270202228. relayout(taskInfo, t, t, true /* applyStartTransactionOnDraw */, - shouldSetTaskVisibilityPositionAndCrop, hasGlobalFocus); + shouldSetTaskVisibilityPositionAndCrop, hasGlobalFocus, displayExclusionRegion); } @VisibleForTesting @@ -198,7 +200,8 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL boolean isStatusBarVisible, boolean isKeyguardVisibleAndOccluded, InsetsState displayInsetsState, - boolean hasGlobalFocus) { + boolean hasGlobalFocus, + @NonNull Region globalExclusionRegion) { relayoutParams.reset(); relayoutParams.mRunningTaskInfo = taskInfo; relayoutParams.mLayoutResId = R.layout.caption_window_decor; @@ -210,6 +213,7 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL relayoutParams.mSetTaskVisibilityPositionAndCrop = shouldSetTaskVisibilityPositionAndCrop; relayoutParams.mIsCaptionVisible = taskInfo.isFreeform() || (isStatusBarVisible && !isKeyguardVisibleAndOccluded); + relayoutParams.mDisplayExclusionRegion.set(globalExclusionRegion); if (TaskInfoKt.isTransparentCaptionBarAppearance(taskInfo)) { // If the app is requesting to customize the caption bar, allow input to fall @@ -236,7 +240,8 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL void relayout(RunningTaskInfo taskInfo, SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT, boolean applyStartTransactionOnDraw, boolean shouldSetTaskVisibilityPositionAndCrop, - boolean hasGlobalFocus) { + boolean hasGlobalFocus, + @NonNull Region globalExclusionRegion) { final boolean isFreeform = taskInfo.getWindowingMode() == WindowConfiguration.WINDOWING_MODE_FREEFORM; final boolean isDragResizeable = ENABLE_WINDOWING_SCALED_RESIZING.isTrue() @@ -249,7 +254,8 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL updateRelayoutParams(mRelayoutParams, taskInfo, applyStartTransactionOnDraw, shouldSetTaskVisibilityPositionAndCrop, mIsStatusBarVisible, mIsKeyguardVisibleAndOccluded, - mDisplayController.getInsetsState(taskInfo.displayId), hasGlobalFocus); + mDisplayController.getInsetsState(taskInfo.displayId), hasGlobalFocus, + globalExclusionRegion); relayout(mRelayoutParams, startT, finishT, wct, oldRootView, mResult); // After this line, mTaskInfo is up-to-date and should be used instead of taskInfo 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 f2d8a782de34..d71e61a4c4de 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 @@ -220,6 +220,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, } mMainExecutor.execute(() -> { mExclusionRegion.set(systemGestureExclusion); + onExclusionRegionChanged(displayId, mExclusionRegion); }); } }; @@ -432,7 +433,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, boolean isFocusedGlobally) { final WindowDecoration decor = mWindowDecorByTaskId.get(taskId); if (decor != null) { - decor.relayout(decor.mTaskInfo, isFocusedGlobally); + decor.relayout(decor.mTaskInfo, isFocusedGlobally, decor.mExclusionRegion); } } @@ -465,15 +466,15 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, final RunningTaskInfo oldTaskInfo = decoration.mTaskInfo; if (taskInfo.displayId != oldTaskInfo.displayId - && !Flags.enableHandleInputFix()) { + && !DesktopModeFlags.ENABLE_HANDLE_INPUT_FIX.isTrue()) { removeTaskFromEventReceiver(oldTaskInfo.displayId); incrementEventReceiverTasks(taskInfo.displayId); } if (enableDisplayFocusInShellTransitions()) { // Pass the current global focus status to avoid updates outside of a ShellTransition. - decoration.relayout(taskInfo, decoration.mHasGlobalFocus); + decoration.relayout(taskInfo, decoration.mHasGlobalFocus, decoration.mExclusionRegion); } else { - decoration.relayout(taskInfo, taskInfo.isFocused); + decoration.relayout(taskInfo, taskInfo.isFocused, decoration.mExclusionRegion); } mActivityOrientationChangeHandler.ifPresent(handler -> handler.handleActivityOrientationChange(oldTaskInfo, taskInfo)); @@ -514,7 +515,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, } else { decoration.relayout(taskInfo, startT, finishT, false /* applyStartTransactionOnDraw */, false /* shouldSetTaskPositionAndCrop */, - mFocusTransitionObserver.hasGlobalFocus(taskInfo)); + mFocusTransitionObserver.hasGlobalFocus(taskInfo), + mExclusionRegion); } } @@ -528,7 +530,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, decoration.relayout(taskInfo, startT, finishT, false /* applyStartTransactionOnDraw */, false /* shouldSetTaskPositionAndCrop */, - mFocusTransitionObserver.hasGlobalFocus(taskInfo)); + mFocusTransitionObserver.hasGlobalFocus(taskInfo), + mExclusionRegion); } @Override @@ -539,7 +542,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, decoration.close(); final int displayId = taskInfo.displayId; if (mEventReceiversByDisplay.contains(displayId) - && !Flags.enableHandleInputFix()) { + && !DesktopModeFlags.ENABLE_HANDLE_INPUT_FIX.isTrue()) { removeTaskFromEventReceiver(displayId); } // Remove the decoration from the cache last because WindowDecoration#close could still @@ -548,6 +551,15 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, mWindowDecorByTaskId.remove(taskInfo.taskId); } + private void onExclusionRegionChanged(int displayId, @NonNull Region exclusionRegion) { + final int decorCount = mWindowDecorByTaskId.size(); + for (int i = 0; i < decorCount; i++) { + final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.valueAt(i); + if (decoration.mTaskInfo.displayId != displayId) continue; + decoration.onExclusionRegionChanged(exclusionRegion); + } + } + private void openHandleMenu(int taskId) { final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(taskId); decoration.createHandleMenu(checkNumberOfOtherInstances(decoration.mTaskInfo) @@ -750,10 +762,13 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, /** * Whether to pilfer the next motion event to send cancellations to the windows below. - * Useful when the caption window is spy and the gesture should be handle by the system + * Useful when the caption window is spy and the gesture should be handled by the system * instead of by the app for their custom header content. + * Should not have any effect when {@link Flags#enableAccessibleCustomHeaders()}, because + * a spy window is not used then. */ - private boolean mShouldPilferCaptionEvents; + private boolean mIsCustomHeaderGesture; + private boolean mIsResizeGesture; private boolean mIsDragging; private boolean mTouchscreenInUse; private boolean mHasLongClicked; @@ -767,7 +782,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, mTaskToken = taskInfo.token; mDragPositioningCallback = dragPositioningCallback; final int touchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop(); - final long appHandleHoldToDragDuration = Flags.enableHoldToDragAppHandle() + final long appHandleHoldToDragDuration = + DesktopModeFlags.ENABLE_HOLD_TO_DRAG_APP_HANDLE.isTrue() ? APP_HANDLE_HOLD_TO_DRAG_DURATION_MS : 0; mHandleDragDetector = new DragDetector(this, appHandleHoldToDragDuration, touchSlop); @@ -867,7 +883,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, // to offset position relative to caption as a whole. int[] viewLocation = new int[2]; v.getLocationInWindow(viewLocation); - final boolean isResizeEvent = decoration.shouldResizeListenerHandleEvent(e, + mIsResizeGesture = decoration.shouldResizeListenerHandleEvent(e, new Point(viewLocation[0], viewLocation[1])); // The caption window may be a spy window when the caption background is // transparent, which means events will fall through to the app window. Make @@ -875,21 +891,23 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, // customizable region and what the app reported as exclusion areas, because // the drag-move or other caption gestures should take priority outside those // regions. - mShouldPilferCaptionEvents = !(downInCustomizableCaptionRegion - && downInExclusionRegion && isTransparentCaption) && !isResizeEvent; + mIsCustomHeaderGesture = downInCustomizableCaptionRegion + && downInExclusionRegion && isTransparentCaption; } - if (!mShouldPilferCaptionEvents) { - // The event will be handled by a window below or pilfered by resize handler. + if (mIsCustomHeaderGesture || mIsResizeGesture) { + // The event will be handled by the custom window below or pilfered by resize + // handler. return false; } - // Otherwise pilfer so that windows below receive cancellations for this gesture, and - // continue normal handling as a caption gesture. - if (mInputManager != null) { + if (mInputManager != null + && !Flags.enableAccessibleCustomHeaders()) { + // Pilfer so that windows below receive cancellations for this gesture. mInputManager.pilferPointers(v.getViewRootImpl().getInputToken()); } if (isUpOrCancel) { // Gesture is finished, reset state. - mShouldPilferCaptionEvents = false; + mIsCustomHeaderGesture = false; + mIsResizeGesture = false; } if (isAppHandle) { return mHandleDragDetector.onMotionEvent(v, e); @@ -1234,7 +1252,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, relevantDecor.updateHoverAndPressStatus(ev); final int action = ev.getActionMasked(); if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) { - if (!mTransitionDragActive && !Flags.enableHandleInputFix()) { + if (!mTransitionDragActive && !DesktopModeFlags.ENABLE_HANDLE_INPUT_FIX.isTrue()) { relevantDecor.closeHandleMenuIfNeeded(ev); } } @@ -1277,7 +1295,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, } final boolean shouldStartTransitionDrag = relevantDecor.checkTouchEventInFocusedCaptionHandle(ev) - || Flags.enableHandleInputFix(); + || DesktopModeFlags.ENABLE_HANDLE_INPUT_FIX.isTrue(); if (dragFromStatusBarAllowed && shouldStartTransitionDrag) { mTransitionDragActive = true; } @@ -1592,8 +1610,9 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, windowDecoration.setDragPositioningCallback(taskPositioner); windowDecoration.relayout(taskInfo, startT, finishT, false /* applyStartTransactionOnDraw */, false /* shouldSetTaskPositionAndCrop */, - mFocusTransitionObserver.hasGlobalFocus(taskInfo)); - if (!Flags.enableHandleInputFix()) { + mFocusTransitionObserver.hasGlobalFocus(taskInfo), + mExclusionRegion); + if (!DesktopModeFlags.ENABLE_HANDLE_INPUT_FIX.isTrue()) { incrementEventReceiverTasks(taskInfo.displayId); } } @@ -1618,6 +1637,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, pw.println(innerPrefix + "mTransitionDragActive=" + mTransitionDragActive); pw.println(innerPrefix + "mEventReceiversByDisplay=" + mEventReceiversByDisplay); pw.println(innerPrefix + "mWindowDecorByTaskId=" + mWindowDecorByTaskId); + pw.println(innerPrefix + "mExclusionRegion=" + mExclusionRegion); } private class DesktopModeOnTaskRepositionAnimationListener @@ -1754,7 +1774,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, && Flags.enableDesktopWindowingImmersiveHandleHiding()) { decor.onInsetsStateChanged(insetsState); } - if (!Flags.enableHandleInputFix()) { + if (!DesktopModeFlags.ENABLE_HANDLE_INPUT_FIX.isTrue()) { // If status bar inset is visible, top task is not in immersive mode. // This value is only needed when the App Handle input is being handled // through the global input monitor (hence the flag check) to ignore gestures 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 d97632a9428c..cdcf14e0cbf3 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 @@ -394,7 +394,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin } @Override - void relayout(ActivityManager.RunningTaskInfo taskInfo, boolean hasGlobalFocus) { + void relayout(ActivityManager.RunningTaskInfo taskInfo, boolean hasGlobalFocus, + @NonNull Region displayExclusionRegion) { final SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get(); // The visibility, crop and position of the task should only be set when a task is // fluid resizing. In all other cases, it is expected that the transition handler sets @@ -415,7 +416,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin // causes flickering. See b/270202228. final boolean applyTransactionOnDraw = taskInfo.isFreeform(); relayout(taskInfo, t, t, applyTransactionOnDraw, shouldSetTaskVisibilityPositionAndCrop, - hasGlobalFocus); + hasGlobalFocus, displayExclusionRegion); if (!applyTransactionOnDraw) { t.apply(); } @@ -442,18 +443,18 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin void relayout(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT, boolean applyStartTransactionOnDraw, boolean shouldSetTaskVisibilityPositionAndCrop, - boolean hasGlobalFocus) { + boolean hasGlobalFocus, @NonNull Region displayExclusionRegion) { Trace.beginSection("DesktopModeWindowDecoration#relayout"); if (taskInfo.isFreeform()) { // The Task is in Freeform mode -> show its header in sync since it's an integral part // of the window itself - a delayed header might cause bad UX. relayoutInSync(taskInfo, startT, finishT, applyStartTransactionOnDraw, - shouldSetTaskVisibilityPositionAndCrop, hasGlobalFocus); + shouldSetTaskVisibilityPositionAndCrop, hasGlobalFocus, displayExclusionRegion); } else { // The Task is outside Freeform mode -> allow the handle view to be delayed since the // handle is just a small addition to the window. relayoutWithDelayedViewHost(taskInfo, startT, finishT, applyStartTransactionOnDraw, - shouldSetTaskVisibilityPositionAndCrop, hasGlobalFocus); + shouldSetTaskVisibilityPositionAndCrop, hasGlobalFocus, displayExclusionRegion); } Trace.endSection(); } @@ -462,11 +463,11 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin private void relayoutInSync(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT, boolean applyStartTransactionOnDraw, boolean shouldSetTaskVisibilityPositionAndCrop, - boolean hasGlobalFocus) { + boolean hasGlobalFocus, @NonNull Region displayExclusionRegion) { // Clear the current ViewHost runnable as we will update the ViewHost here clearCurrentViewHostRunnable(); updateRelayoutParamsAndSurfaces(taskInfo, startT, finishT, applyStartTransactionOnDraw, - shouldSetTaskVisibilityPositionAndCrop, hasGlobalFocus); + shouldSetTaskVisibilityPositionAndCrop, hasGlobalFocus, displayExclusionRegion); if (mResult.mRootView != null) { updateViewHost(mRelayoutParams, startT, mResult); } @@ -489,7 +490,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin private void relayoutWithDelayedViewHost(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT, boolean applyStartTransactionOnDraw, boolean shouldSetTaskVisibilityPositionAndCrop, - boolean hasGlobalFocus) { + boolean hasGlobalFocus, + @NonNull Region displayExclusionRegion) { if (applyStartTransactionOnDraw) { throw new IllegalArgumentException( "We cannot both sync viewhost ondraw and delay viewhost creation."); @@ -498,7 +500,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin clearCurrentViewHostRunnable(); updateRelayoutParamsAndSurfaces(taskInfo, startT, finishT, false /* applyStartTransactionOnDraw */, shouldSetTaskVisibilityPositionAndCrop, - hasGlobalFocus); + hasGlobalFocus, displayExclusionRegion); if (mResult.mRootView == null) { // This means something blocks the window decor from showing, e.g. the task is hidden. // Nothing is set up in this case including the decoration surface. @@ -513,7 +515,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin private void updateRelayoutParamsAndSurfaces(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT, boolean applyStartTransactionOnDraw, boolean shouldSetTaskVisibilityPositionAndCrop, - boolean hasGlobalFocus) { + boolean hasGlobalFocus, @NonNull Region displayExclusionRegion) { Trace.beginSection("DesktopModeWindowDecoration#updateRelayoutParamsAndSurfaces"); if (Flags.enableDesktopWindowingAppToWeb()) { setCapturedLink(taskInfo.capturedLink, taskInfo.capturedLinkTimestamp); @@ -538,7 +540,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin updateRelayoutParams(mRelayoutParams, mContext, taskInfo, applyStartTransactionOnDraw, shouldSetTaskVisibilityPositionAndCrop, mIsStatusBarVisible, mIsKeyguardVisibleAndOccluded, inFullImmersive, - mDisplayController.getInsetsState(taskInfo.displayId), hasGlobalFocus); + mDisplayController.getInsetsState(taskInfo.displayId), hasGlobalFocus, + displayExclusionRegion); final WindowDecorLinearLayout oldRootView = mResult.mRootView; final SurfaceControl oldDecorationSurface = mDecorationContainerSurface; @@ -628,13 +631,6 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin @Nullable private Intent getBrowserLink() { - // Do not show browser link in browser applications - final ComponentName baseActivity = mTaskInfo.baseActivity; - if (baseActivity != null && AppToWebUtils.isBrowserApp(mContext, - baseActivity.getPackageName(), mUserContext.getUserId())) { - return null; - } - final Uri browserLink; // If the captured link is available and has not expired, return the captured link. // Otherwise, return the generic link which is set to null if a generic link is unavailable. @@ -651,6 +647,18 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin } + @Nullable + private Intent getAppLink() { + return mWebUri == null ? null + : AppToWebUtils.getAppIntent(mWebUri, mContext.getPackageManager()); + } + + private boolean isBrowserApp() { + final ComponentName baseActivity = mTaskInfo.baseActivity; + return baseActivity != null && AppToWebUtils.isBrowserApp(mContext, + baseActivity.getPackageName(), mUserContext.getUserId()); + } + UserHandle getUser() { return mUserContext.getUser(); } @@ -807,7 +815,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin */ void disposeStatusBarInputLayer() { if (!isAppHandle(mWindowDecorViewHolder) - || !Flags.enableHandleInputFix()) { + || !DesktopModeFlags.ENABLE_HANDLE_INPUT_FIX.isTrue()) { return; } asAppHandle(mWindowDecorViewHolder).disposeStatusBarInputLayer(); @@ -874,7 +882,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin boolean isKeyguardVisibleAndOccluded, boolean inFullImmersiveMode, @NonNull InsetsState displayInsetsState, - boolean hasGlobalFocus) { + boolean hasGlobalFocus, + @NonNull Region displayExclusionRegion) { final int captionLayoutId = getDesktopModeWindowDecorLayoutId(taskInfo.getWindowingMode()); final boolean isAppHeader = captionLayoutId == R.layout.desktop_mode_app_header; @@ -885,6 +894,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin relayoutParams.mCaptionHeightId = getCaptionHeightIdStatic(taskInfo.getWindowingMode()); relayoutParams.mCaptionWidthId = getCaptionWidthId(relayoutParams.mLayoutResId); relayoutParams.mHasGlobalFocus = hasGlobalFocus; + relayoutParams.mDisplayExclusionRegion.set(displayExclusionRegion); final boolean showCaption; if (Flags.enableFullyImmersiveInDesktop()) { @@ -910,10 +920,20 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin relayoutParams.mIsInsetSource = isAppHeader && !inFullImmersiveMode; if (isAppHeader) { if (TaskInfoKt.isTransparentCaptionBarAppearance(taskInfo)) { - // If the app is requesting to customize the caption bar, allow input to fall - // through to the windows below so that the app can respond to input events on - // their custom content. - relayoutParams.mInputFeatures |= WindowManager.LayoutParams.INPUT_FEATURE_SPY; + // The app is requesting to customize the caption bar, which means input on + // customizable/exclusion regions must go to the app instead of to the system. + // This may be accomplished with spy windows or custom touchable regions: + if (Flags.enableAccessibleCustomHeaders()) { + // Set the touchable region of the caption to only the areas where input should + // be handled by the system (i.e. non custom-excluded areas). The region will + // be calculated based on occluding caption elements and exclusion areas + // reported by the app. + relayoutParams.mLimitTouchRegionToSystemAreas = true; + } else { + // Allow input to fall through to the windows below so that the app can respond + // to input events on their custom content. + relayoutParams.mInputFeatures |= WindowManager.LayoutParams.INPUT_FEATURE_SPY; + } } else { if (ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION.isTrue()) { // Force-consume the caption bar insets when the app tries to hide the caption. @@ -951,7 +971,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin } controlsElement.mAlignment = RelayoutParams.OccludingCaptionElement.Alignment.END; relayoutParams.mOccludingCaptionElements.add(controlsElement); - } else if (isAppHandle && !Flags.enableHandleInputFix()) { + } else if (isAppHandle && !DesktopModeFlags.ENABLE_HANDLE_INPUT_FIX.isTrue()) { // The focused decor (fullscreen/split) does not need to handle input because input in // the App Handle is handled by the InputMonitor in DesktopModeWindowDecorViewModel. // Note: This does not apply with the above flag enabled as the status bar input layer @@ -1368,6 +1388,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin .shouldShowChangeAspectRatioButton(mTaskInfo); final boolean inDesktopImmersive = mDesktopRepository .isTaskInFullImmersiveState(mTaskInfo.taskId); + final boolean isBrowserApp = isBrowserApp(); mHandleMenu = mHandleMenuFactory.create( this, mWindowManagerWrapper, @@ -1379,7 +1400,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin supportsMultiInstance, shouldShowManageWindowsButton, shouldShowChangeAspectRatioButton, - getBrowserLink(), + isBrowserApp, + isBrowserApp ? getAppLink() : getBrowserLink(), mResult.mCaptionWidth, mResult.mCaptionHeight, mResult.mCaptionX, @@ -1560,13 +1582,13 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin */ boolean checkTouchEventInFocusedCaptionHandle(MotionEvent ev) { if (isHandleMenuActive() || !isAppHandle(mWindowDecorViewHolder) - || Flags.enableHandleInputFix()) { + || DesktopModeFlags.ENABLE_HANDLE_INPUT_FIX.isTrue()) { return false; } // The status bar input layer can only receive input in handle coordinates to begin with, // so checking coordinates is unnecessary as input is always within handle bounds. if (isAppHandle(mWindowDecorViewHolder) - && Flags.enableHandleInputFix() + && DesktopModeFlags.ENABLE_HANDLE_INPUT_FIX.isTrue() && isCaptionVisible()) { return true; } @@ -1603,7 +1625,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin * @param ev the MotionEvent to compare */ void checkTouchEvent(MotionEvent ev) { - if (mResult.mRootView == null || Flags.enableHandleInputFix()) return; + if (mResult.mRootView == null || DesktopModeFlags.ENABLE_HANDLE_INPUT_FIX.isTrue()) return; final View caption = mResult.mRootView.findViewById(R.id.desktop_mode_caption); final View handle = caption.findViewById(R.id.caption_handle); final boolean inHandle = !isHandleMenuActive() @@ -1616,7 +1638,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin // If the whole handle menu can be touched directly, rely on FLAG_WATCH_OUTSIDE_TOUCH. // This is for the case that some of the handle menu is underneath the status bar. if (isAppHandle(mWindowDecorViewHolder) - && !Flags.enableHandleInputFix()) { + && !DesktopModeFlags.ENABLE_HANDLE_INPUT_FIX.isTrue()) { mHandleMenu.checkMotionEvent(ev); closeHandleMenuIfNeeded(ev); } @@ -1630,7 +1652,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin * @param ev the MotionEvent to compare against. */ void updateHoverAndPressStatus(MotionEvent ev) { - if (mResult.mRootView == null || Flags.enableHandleInputFix()) return; + if (mResult.mRootView == null || DesktopModeFlags.ENABLE_HANDLE_INPUT_FIX.isTrue()) return; final View handle = mResult.mRootView.findViewById(R.id.caption_handle); final boolean inHandle = !isHandleMenuActive() && checkTouchEventInFocusedCaptionHandle(ev); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt index 2edc380756ac..54c247bff984 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt @@ -39,12 +39,14 @@ import android.widget.Button import android.widget.ImageButton import android.widget.ImageView import android.widget.TextView +import android.window.DesktopModeFlags import android.window.SurfaceSyncGroup +import androidx.annotation.StringRes import androidx.annotation.VisibleForTesting import androidx.compose.ui.graphics.toArgb import androidx.core.view.isGone -import com.android.window.flags.Flags import com.android.wm.shell.R +import com.android.wm.shell.apptoweb.isBrowserApp import com.android.wm.shell.shared.split.SplitScreenConstants import com.android.wm.shell.splitscreen.SplitScreenController import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer @@ -73,7 +75,8 @@ class HandleMenu( private val shouldShowNewWindowButton: Boolean, private val shouldShowManageWindowsButton: Boolean, private val shouldShowChangeAspectRatioButton: Boolean, - private val openInBrowserIntent: Intent?, + private val isBrowserApp: Boolean, + private val openInAppOrBrowserIntent: Intent?, private val captionWidth: Int, private val captionHeight: Int, captionX: Int, @@ -83,7 +86,7 @@ class HandleMenu( private val taskInfo: RunningTaskInfo = parentDecor.mTaskInfo private val isViewAboveStatusBar: Boolean - get() = (Flags.enableHandleInputFix() && !taskInfo.isFreeform) + get() = (DesktopModeFlags.ENABLE_HANDLE_INPUT_FIX.isTrue() && !taskInfo.isFreeform) private val pillElevation: Int = loadDimensionPixelSize( R.dimen.desktop_mode_handle_menu_pill_elevation) @@ -111,7 +114,7 @@ class HandleMenu( private val globalMenuPosition: Point = Point() private val shouldShowBrowserPill: Boolean - get() = openInBrowserIntent != null + get() = openInAppOrBrowserIntent != null private val shouldShowMoreActionsPill: Boolean get() = SHOULD_SHOW_SCREENSHOT_BUTTON || shouldShowNewWindowButton || @@ -128,7 +131,7 @@ class HandleMenu( onNewWindowClickListener: () -> Unit, onManageWindowsClickListener: () -> Unit, onChangeAspectRatioClickListener: () -> Unit, - openInBrowserClickListener: (Intent) -> Unit, + openInAppOrBrowserClickListener: (Intent) -> Unit, onOpenByDefaultClickListener: () -> Unit, onCloseMenuClickListener: () -> Unit, onOutsideTouchListener: () -> Unit, @@ -146,7 +149,7 @@ class HandleMenu( onNewWindowClickListener = onNewWindowClickListener, onManageWindowsClickListener = onManageWindowsClickListener, onChangeAspectRatioClickListener = onChangeAspectRatioClickListener, - openInBrowserClickListener = openInBrowserClickListener, + openInAppOrBrowserClickListener = openInAppOrBrowserClickListener, onOpenByDefaultClickListener = onOpenByDefaultClickListener, onCloseMenuClickListener = onCloseMenuClickListener, onOutsideTouchListener = onOutsideTouchListener, @@ -167,7 +170,7 @@ class HandleMenu( onNewWindowClickListener: () -> Unit, onManageWindowsClickListener: () -> Unit, onChangeAspectRatioClickListener: () -> Unit, - openInBrowserClickListener: (Intent) -> Unit, + openInAppOrBrowserClickListener: (Intent) -> Unit, onOpenByDefaultClickListener: () -> Unit, onCloseMenuClickListener: () -> Unit, onOutsideTouchListener: () -> Unit, @@ -181,7 +184,8 @@ class HandleMenu( shouldShowBrowserPill = shouldShowBrowserPill, shouldShowNewWindowButton = shouldShowNewWindowButton, shouldShowManageWindowsButton = shouldShowManageWindowsButton, - shouldShowChangeAspectRatioButton = shouldShowChangeAspectRatioButton + shouldShowChangeAspectRatioButton = shouldShowChangeAspectRatioButton, + isBrowserApp = isBrowserApp ).apply { bind(taskInfo, appIconBitmap, appName, shouldShowMoreActionsPill) this.onToDesktopClickListener = onToDesktopClickListener @@ -190,8 +194,8 @@ class HandleMenu( this.onNewWindowClickListener = onNewWindowClickListener this.onManageWindowsClickListener = onManageWindowsClickListener this.onChangeAspectRatioClickListener = onChangeAspectRatioClickListener - this.onOpenInBrowserClickListener = { - openInBrowserClickListener.invoke(openInBrowserIntent!!) + this.onOpenInAppOrBrowserClickListener = { + openInAppOrBrowserClickListener.invoke(openInAppOrBrowserIntent!!) } this.onOpenByDefaultClickListener = onOpenByDefaultClickListener this.onCloseMenuClickListener = onCloseMenuClickListener @@ -201,7 +205,8 @@ class HandleMenu( val x = handleMenuPosition.x.toInt() val y = handleMenuPosition.y.toInt() handleMenuViewContainer = - if ((!taskInfo.isFreeform && Flags.enableHandleInputFix()) || forceShowSystemBars) { + if ((!taskInfo.isFreeform && DesktopModeFlags.ENABLE_HANDLE_INPUT_FIX.isTrue()) + || forceShowSystemBars) { AdditionalSystemViewContainer( windowManagerWrapper = windowManagerWrapper, taskId = taskInfo.taskId, @@ -237,7 +242,7 @@ class HandleMenu( menuX = marginMenuStart menuY = captionY + marginMenuTop } else { - if (Flags.enableHandleInputFix()) { + if (DesktopModeFlags.ENABLE_HANDLE_INPUT_FIX.isTrue()) { // In a focused decor, we use global coordinates for handle menu. Therefore we // need to account for other factors like split stage and menu/handle width to // center the menu. @@ -435,14 +440,15 @@ class HandleMenu( /** The view within the Handle Menu, with options to change the windowing mode and more. */ @SuppressLint("ClickableViewAccessibility") class HandleMenuView( - context: Context, + private val context: Context, menuWidth: Int, captionHeight: Int, private val shouldShowWindowingPill: Boolean, private val shouldShowBrowserPill: Boolean, private val shouldShowNewWindowButton: Boolean, private val shouldShowManageWindowsButton: Boolean, - private val shouldShowChangeAspectRatioButton: Boolean + private val shouldShowChangeAspectRatioButton: Boolean, + private val isBrowserApp: Boolean ) { val rootView = LayoutInflater.from(context) .inflate(R.layout.desktop_mode_window_decor_handle_menu, null /* root */) as View @@ -472,11 +478,12 @@ class HandleMenu( private val changeAspectRatioBtn = moreActionsPill .requireViewById<Button>(R.id.change_aspect_ratio_button) - // Open in Browser Pill. - private val openInBrowserPill = rootView.requireViewById<View>(R.id.open_in_browser_pill) - private val browserBtn = openInBrowserPill.requireViewById<Button>( - R.id.open_in_browser_button) - private val openByDefaultBtn = openInBrowserPill.requireViewById<ImageButton>( + // Open in Browser/App Pill. + private val openInAppOrBrowserPill = rootView.requireViewById<View>( + R.id.open_in_app_or_browser_pill) + private val openInAppOrBrowserBtn = openInAppOrBrowserPill.requireViewById<Button>( + R.id.open_in_app_or_browser_button) + private val openByDefaultBtn = openInAppOrBrowserPill.requireViewById<ImageButton>( R.id.open_by_default_button) private val decorThemeUtil = DecorThemeUtil(context) private val animator = HandleMenuAnimator(rootView, menuWidth, captionHeight.toFloat()) @@ -490,7 +497,7 @@ class HandleMenu( var onNewWindowClickListener: (() -> Unit)? = null var onManageWindowsClickListener: (() -> Unit)? = null var onChangeAspectRatioClickListener: (() -> Unit)? = null - var onOpenInBrowserClickListener: (() -> Unit)? = null + var onOpenInAppOrBrowserClickListener: (() -> Unit)? = null var onOpenByDefaultClickListener: (() -> Unit)? = null var onCloseMenuClickListener: (() -> Unit)? = null var onOutsideTouchListener: (() -> Unit)? = null @@ -499,7 +506,7 @@ class HandleMenu( fullscreenBtn.setOnClickListener { onToFullscreenClickListener?.invoke() } splitscreenBtn.setOnClickListener { onToSplitScreenClickListener?.invoke() } desktopBtn.setOnClickListener { onToDesktopClickListener?.invoke() } - browserBtn.setOnClickListener { onOpenInBrowserClickListener?.invoke() } + openInAppOrBrowserBtn.setOnClickListener { onOpenInAppOrBrowserClickListener?.invoke() } openByDefaultBtn.setOnClickListener { onOpenByDefaultClickListener?.invoke() } @@ -535,10 +542,10 @@ class HandleMenu( if (shouldShowMoreActionsPill) { bindMoreActionsPill(style) } - bindOpenInBrowserPill(style) + bindOpenInAppOrBrowserPill(style) } - /** Animates the menu opening. */ + /** Animates the menu openInAppOrBrowserg. */ fun animateOpenMenu() { if (taskInfo.isFullscreen || taskInfo.isMultiWindow) { animator.animateCaptionHandleExpandToOpen() @@ -660,13 +667,20 @@ class HandleMenu( } } - private fun bindOpenInBrowserPill(style: MenuStyle) { - openInBrowserPill.apply { + private fun bindOpenInAppOrBrowserPill(style: MenuStyle) { + openInAppOrBrowserPill.apply { isGone = !shouldShowBrowserPill background.setTint(style.backgroundColor) } - browserBtn.apply { + val btnText = if (isBrowserApp) { + getString(R.string.open_in_app_text) + } else { + getString(R.string.open_in_browser_text) + } + openInAppOrBrowserBtn.apply { + text = btnText + contentDescription = btnText setTextColor(style.textColor) compoundDrawableTintList = ColorStateList.valueOf(style.textColor) } @@ -674,6 +688,8 @@ class HandleMenu( openByDefaultBtn.imageTintList = ColorStateList.valueOf(style.textColor) } + private fun getString(@StringRes resId: Int): String = context.resources.getString(resId) + private data class MenuStyle( @ColorInt val backgroundColor: Int, @ColorInt val textColor: Int, @@ -708,7 +724,8 @@ interface HandleMenuFactory { shouldShowNewWindowButton: Boolean, shouldShowManageWindowsButton: Boolean, shouldShowChangeAspectRatioButton: Boolean, - openInBrowserIntent: Intent?, + isBrowserApp: Boolean, + openInAppOrBrowserIntent: Intent?, captionWidth: Int, captionHeight: Int, captionX: Int, @@ -729,7 +746,8 @@ object DefaultHandleMenuFactory : HandleMenuFactory { shouldShowNewWindowButton: Boolean, shouldShowManageWindowsButton: Boolean, shouldShowChangeAspectRatioButton: Boolean, - openInBrowserIntent: Intent?, + isBrowserApp: Boolean, + openInAppOrBrowserIntent: Intent?, captionWidth: Int, captionHeight: Int, captionX: Int, @@ -746,7 +764,8 @@ object DefaultHandleMenuFactory : HandleMenuFactory { shouldShowNewWindowButton, shouldShowManageWindowsButton, shouldShowChangeAspectRatioButton, - openInBrowserIntent, + isBrowserApp, + openInAppOrBrowserIntent, captionWidth, captionHeight, captionX, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuAnimator.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuAnimator.kt index 0c475f12f53b..470e5a1d88b4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuAnimator.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuAnimator.kt @@ -74,7 +74,8 @@ class HandleMenuAnimator( private val appInfoPill: ViewGroup = handleMenu.requireViewById(R.id.app_info_pill) private val windowingPill: ViewGroup = handleMenu.requireViewById(R.id.windowing_pill) private val moreActionsPill: ViewGroup = handleMenu.requireViewById(R.id.more_actions_pill) - private val openInBrowserPill: ViewGroup = handleMenu.requireViewById(R.id.open_in_browser_pill) + private val openInAppOrBrowserPill: ViewGroup = + handleMenu.requireViewById(R.id.open_in_app_or_browser_pill) /** Animates the opening of the handle menu. */ fun animateOpen() { @@ -83,7 +84,7 @@ class HandleMenuAnimator( animateAppInfoPillOpen() animateWindowingPillOpen() animateMoreActionsPillOpen() - animateOpenInBrowserPill() + animateOpenInAppOrBrowserPill() runAnimations { appInfoPill.post { appInfoPill.requireViewById<View>(R.id.collapse_menu_button).sendAccessibilityEvent( @@ -103,7 +104,7 @@ class HandleMenuAnimator( animateAppInfoPillOpen() animateWindowingPillOpen() animateMoreActionsPillOpen() - animateOpenInBrowserPill() + animateOpenInAppOrBrowserPill() runAnimations { appInfoPill.post { appInfoPill.requireViewById<View>(R.id.collapse_menu_button).sendAccessibilityEvent( @@ -124,7 +125,7 @@ class HandleMenuAnimator( animateAppInfoPillFadeOut() windowingPillClose() moreActionsPillClose() - openInBrowserPillClose() + openInAppOrBrowserPillClose() runAnimations(after) } @@ -141,7 +142,7 @@ class HandleMenuAnimator( animateAppInfoPillFadeOut() windowingPillClose() moreActionsPillClose() - openInBrowserPillClose() + openInAppOrBrowserPillClose() runAnimations(after) } @@ -154,7 +155,7 @@ class HandleMenuAnimator( appInfoPill.children.forEach { it.alpha = 0f } windowingPill.alpha = 0f moreActionsPill.alpha = 0f - openInBrowserPill.alpha = 0f + openInAppOrBrowserPill.alpha = 0f // Setup pivots. handleMenu.pivotX = menuWidth / 2f @@ -166,8 +167,8 @@ class HandleMenuAnimator( moreActionsPill.pivotX = menuWidth / 2f moreActionsPill.pivotY = appInfoPill.measuredHeight.toFloat() - openInBrowserPill.pivotX = menuWidth / 2f - openInBrowserPill.pivotY = appInfoPill.measuredHeight.toFloat() + openInAppOrBrowserPill.pivotX = menuWidth / 2f + openInAppOrBrowserPill.pivotY = appInfoPill.measuredHeight.toFloat() } private fun animateAppInfoPillOpen() { @@ -297,36 +298,36 @@ class HandleMenuAnimator( } } - private fun animateOpenInBrowserPill() { + private fun animateOpenInAppOrBrowserPill() { // Open in Browser X & Y Scaling Animation animators += - ObjectAnimator.ofFloat(openInBrowserPill, SCALE_X, HALF_INITIAL_SCALE, 1f).apply { + ObjectAnimator.ofFloat(openInAppOrBrowserPill, SCALE_X, HALF_INITIAL_SCALE, 1f).apply { startDelay = BODY_SCALE_OPEN_DELAY duration = BODY_SCALE_OPEN_DURATION } animators += - ObjectAnimator.ofFloat(openInBrowserPill, SCALE_Y, HALF_INITIAL_SCALE, 1f).apply { + ObjectAnimator.ofFloat(openInAppOrBrowserPill, SCALE_Y, HALF_INITIAL_SCALE, 1f).apply { startDelay = BODY_SCALE_OPEN_DELAY duration = BODY_SCALE_OPEN_DURATION } // Open in Browser Opacity Animation animators += - ObjectAnimator.ofFloat(openInBrowserPill, ALPHA, 1f).apply { + ObjectAnimator.ofFloat(openInAppOrBrowserPill, ALPHA, 1f).apply { startDelay = BODY_ALPHA_OPEN_DELAY duration = BODY_ALPHA_OPEN_DURATION } // Open in Browser Elevation Animation animators += - ObjectAnimator.ofFloat(openInBrowserPill, TRANSLATION_Z, 1f).apply { + ObjectAnimator.ofFloat(openInAppOrBrowserPill, TRANSLATION_Z, 1f).apply { startDelay = ELEVATION_OPEN_DELAY duration = BODY_ELEVATION_OPEN_DURATION } // Open in Browser Button Opacity Animation - val button = openInBrowserPill.requireViewById<Button>(R.id.open_in_browser_button) + val button = openInAppOrBrowserPill.requireViewById<Button>(R.id.open_in_app_or_browser_button) animators += ObjectAnimator.ofFloat(button, ALPHA, 1f).apply { startDelay = BODY_ALPHA_OPEN_DELAY @@ -438,33 +439,33 @@ class HandleMenuAnimator( } } - private fun openInBrowserPillClose() { + private fun openInAppOrBrowserPillClose() { // Open in Browser X & Y Scaling Animation animators += - ObjectAnimator.ofFloat(openInBrowserPill, SCALE_X, HALF_INITIAL_SCALE).apply { + ObjectAnimator.ofFloat(openInAppOrBrowserPill, SCALE_X, HALF_INITIAL_SCALE).apply { duration = BODY_CLOSE_DURATION } animators += - ObjectAnimator.ofFloat(openInBrowserPill, SCALE_Y, HALF_INITIAL_SCALE).apply { + ObjectAnimator.ofFloat(openInAppOrBrowserPill, SCALE_Y, HALF_INITIAL_SCALE).apply { duration = BODY_CLOSE_DURATION } // Open in Browser Opacity Animation animators += - ObjectAnimator.ofFloat(openInBrowserPill, ALPHA, 0f).apply { + ObjectAnimator.ofFloat(openInAppOrBrowserPill, ALPHA, 0f).apply { duration = BODY_CLOSE_DURATION } animators += - ObjectAnimator.ofFloat(openInBrowserPill, ALPHA, 0f).apply { + ObjectAnimator.ofFloat(openInAppOrBrowserPill, ALPHA, 0f).apply { duration = BODY_CLOSE_DURATION } // Upward Open in Browser y-translation Animation val yStart: Float = -captionHeight / 2 animators += - ObjectAnimator.ofFloat(openInBrowserPill, TRANSLATION_Y, yStart).apply { + ObjectAnimator.ofFloat(openInAppOrBrowserPill, TRANSLATION_Y, yStart).apply { duration = BODY_CLOSE_DURATION } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuImageButton.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuImageButton.kt index cf82bb4f9919..8bc56e0807a8 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuImageButton.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuImageButton.kt @@ -16,13 +16,12 @@ package com.android.wm.shell.windowdecor import android.app.ActivityManager.RunningTaskInfo -import com.android.window.flags.Flags -import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer - import android.content.Context import android.util.AttributeSet import android.view.MotionEvent import android.widget.ImageButton +import android.window.DesktopModeFlags +import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer /** * A custom [ImageButton] for buttons inside handle menu that intentionally doesn't handle hovers. @@ -39,7 +38,7 @@ class HandleMenuImageButton( lateinit var taskInfo: RunningTaskInfo override fun onHoverEvent(motionEvent: MotionEvent): Boolean { - if (Flags.enableHandleInputFix() || taskInfo.isFreeform) { + if (DesktopModeFlags.ENABLE_HANDLE_INPUT_FIX.isTrue() || taskInfo.isFreeform) { return super.onHoverEvent(motionEvent) } else { return false 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 b016c755e323..a3c75bf33cde 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 @@ -127,7 +127,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> } mDisplayController.removeDisplayWindowListener(this); - relayout(mTaskInfo, mHasGlobalFocus); + relayout(mTaskInfo, mHasGlobalFocus, mExclusionRegion); } }; @@ -143,7 +143,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> SurfaceControl mDecorationContainerSurface; SurfaceControl mCaptionContainerSurface; - private WindowlessWindowManager mCaptionWindowManager; + private CaptionWindowlessWindowManager mCaptionWindowManager; private SurfaceControlViewHost mViewHost; private Configuration mWindowDecorConfig; TaskDragResizer mTaskDragResizer; @@ -152,6 +152,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> boolean mIsStatusBarVisible; boolean mIsKeyguardVisibleAndOccluded; boolean mHasGlobalFocus; + final Region mExclusionRegion = Region.obtain(); /** The most recent set of insets applied to this window decoration. */ private WindowDecorationInsets mWindowDecorationInsets; @@ -218,7 +219,8 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> * constructor. * @param hasGlobalFocus Whether the task is focused */ - abstract void relayout(RunningTaskInfo taskInfo, boolean hasGlobalFocus); + abstract void relayout(RunningTaskInfo taskInfo, boolean hasGlobalFocus, + @NonNull Region displayExclusionRegion); /** * Used by the {@link DragPositioningCallback} associated with the implementing class to @@ -244,6 +246,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> mTaskInfo = params.mRunningTaskInfo; } mHasGlobalFocus = params.mHasGlobalFocus; + mExclusionRegion.set(params.mDisplayExclusionRegion); final int oldLayoutResId = mLayoutResId; mLayoutResId = params.mLayoutResId; @@ -402,7 +405,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> final int elementWidthPx = resources.getDimensionPixelSize(element.mWidthResId); boundingRects[i] = - calculateBoundingRect(element, elementWidthPx, captionInsetsRect); + calculateBoundingRectLocal(element, elementWidthPx, captionInsetsRect); // Subtract the regions used by the caption elements, the rest is // customizable. if (params.hasInputFeatureSpy()) { @@ -477,9 +480,8 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> if (mCaptionWindowManager == null) { // Put caption under a container surface because ViewRootImpl sets the destination frame // of windowless window layers and BLASTBufferQueue#update() doesn't support offset. - mCaptionWindowManager = new WindowlessWindowManager( - mTaskInfo.getConfiguration(), mCaptionContainerSurface, - null /* hostInputToken */); + mCaptionWindowManager = new CaptionWindowlessWindowManager( + mTaskInfo.getConfiguration(), mCaptionContainerSurface); } mCaptionWindowManager.setConfiguration(mTaskInfo.getConfiguration()); final WindowManager.LayoutParams lp = @@ -492,6 +494,14 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> lp.setTitle("Caption of Task=" + mTaskInfo.taskId); lp.setTrustedOverlay(); lp.inputFeatures = params.mInputFeatures; + final Rect localCaptionBounds = new Rect( + outResult.mCaptionX, + outResult.mCaptionY, + outResult.mCaptionX + outResult.mCaptionWidth, + outResult.mCaptionY + outResult.mCaptionHeight); + final Region touchableRegion = params.mLimitTouchRegionToSystemAreas + ? calculateLimitedTouchableRegion(params, localCaptionBounds) + : null; if (mViewHost == null) { Trace.beginSection("CaptionViewHostLayout-new"); mViewHost = mSurfaceControlViewHostFactory.create(mDecorWindowContext, mDisplay, @@ -503,6 +513,9 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> mViewHost.getRootSurfaceControl().applyTransactionOnDraw(onDrawTransaction); } outResult.mRootView.setPadding(0, params.mCaptionTopPadding, 0, 0); + if (params.mLimitTouchRegionToSystemAreas) { + mCaptionWindowManager.setTouchRegion(mViewHost, touchableRegion); + } mViewHost.setView(outResult.mRootView, lp); Trace.endSection(); } else { @@ -514,13 +527,71 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> mViewHost.getRootSurfaceControl().applyTransactionOnDraw(onDrawTransaction); } outResult.mRootView.setPadding(0, params.mCaptionTopPadding, 0, 0); + if (params.mLimitTouchRegionToSystemAreas) { + mCaptionWindowManager.setTouchRegion(mViewHost, touchableRegion); + } mViewHost.relayout(lp); Trace.endSection(); } + if (touchableRegion != null) { + touchableRegion.recycle(); + } Trace.endSection(); // CaptionViewHostLayout } - private Rect calculateBoundingRect(@NonNull OccludingCaptionElement element, + @NonNull + private Region calculateLimitedTouchableRegion( + RelayoutParams params, + @NonNull Rect localCaptionBounds) { + // Make caption bounds relative to display to align with exclusion region. + final Point positionInParent = params.mRunningTaskInfo.positionInParent; + final Rect captionBoundsInDisplay = new Rect(localCaptionBounds); + captionBoundsInDisplay.offsetTo(positionInParent.x, positionInParent.y); + + final Region boundingRects = calculateBoundingRectsRegion(params, captionBoundsInDisplay); + + final Region customizedRegion = Region.obtain(); + customizedRegion.set(captionBoundsInDisplay); + customizedRegion.op(boundingRects, Region.Op.DIFFERENCE); + customizedRegion.op(params.mDisplayExclusionRegion, Region.Op.INTERSECT); + + final Region touchableRegion = Region.obtain(); + touchableRegion.set(captionBoundsInDisplay); + touchableRegion.op(customizedRegion, Region.Op.DIFFERENCE); + // Return resulting region back to window coordinates. + touchableRegion.translate(-positionInParent.x, -positionInParent.y); + + boundingRects.recycle(); + customizedRegion.recycle(); + return touchableRegion; + } + + @NonNull + private Region calculateBoundingRectsRegion( + @NonNull RelayoutParams params, + @NonNull Rect captionBoundsInDisplay) { + final int numOfElements = params.mOccludingCaptionElements.size(); + final Region region = Region.obtain(); + if (numOfElements == 0) { + // The entire caption is a bounding rect. + region.set(captionBoundsInDisplay); + return region; + } + final Resources resources = mDecorWindowContext.getResources(); + for (int i = 0; i < numOfElements; i++) { + final OccludingCaptionElement element = params.mOccludingCaptionElements.get(i); + final int elementWidthPx = resources.getDimensionPixelSize(element.mWidthResId); + final Rect boundingRect = calculateBoundingRectLocal(element, elementWidthPx, + captionBoundsInDisplay); + // Bounding rect is initially calculated relative to the caption, so offset it to make + // it relative to the display. + boundingRect.offset(captionBoundsInDisplay.left, captionBoundsInDisplay.top); + region.union(boundingRect); + } + return region; + } + + private Rect calculateBoundingRectLocal(@NonNull OccludingCaptionElement element, int elementWidthPx, @NonNull Rect captionRect) { switch (element.mAlignment) { case START -> { @@ -539,7 +610,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> mIsKeyguardVisibleAndOccluded = visible && occluded; final boolean changed = prevVisAndOccluded != mIsKeyguardVisibleAndOccluded; if (changed) { - relayout(mTaskInfo, mHasGlobalFocus); + relayout(mTaskInfo, mHasGlobalFocus, mExclusionRegion); } } @@ -549,10 +620,14 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> final boolean changed = prevStatusBarVisibility != mIsStatusBarVisible; if (changed) { - relayout(mTaskInfo, mHasGlobalFocus); + relayout(mTaskInfo, mHasGlobalFocus, mExclusionRegion); } } + void onExclusionRegionChanged(@NonNull Region exclusionRegion) { + relayout(mTaskInfo, mHasGlobalFocus, exclusionRegion); + } + /** * Update caption visibility state and views. */ @@ -751,9 +826,11 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> int mCaptionHeightId; int mCaptionWidthId; final List<OccludingCaptionElement> mOccludingCaptionElements = new ArrayList<>(); + boolean mLimitTouchRegionToSystemAreas; int mInputFeatures; boolean mIsInsetSource = true; @InsetsSource.Flags int mInsetSourceFlags; + final Region mDisplayExclusionRegion = Region.obtain(); int mShadowRadiusId; int mCornerRadius; @@ -772,9 +849,11 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> mCaptionHeightId = Resources.ID_NULL; mCaptionWidthId = Resources.ID_NULL; mOccludingCaptionElements.clear(); + mLimitTouchRegionToSystemAreas = false; mInputFeatures = 0; mIsInsetSource = true; mInsetSourceFlags = 0; + mDisplayExclusionRegion.setEmpty(); mShadowRadiusId = Resources.ID_NULL; mCornerRadius = 0; @@ -830,6 +909,19 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> } } + private static class CaptionWindowlessWindowManager extends WindowlessWindowManager { + CaptionWindowlessWindowManager( + @NonNull Configuration configuration, + @NonNull SurfaceControl rootSurface) { + super(configuration, rootSurface, /* hostInputToken= */ null); + } + + /** Set the view host's touchable region. */ + void setTouchRegion(@NonNull SurfaceControlViewHost viewHost, @NonNull Region region) { + setTouchRegion(viewHost.getWindowToken().asBinder(), region); + } + } + @VisibleForTesting public interface SurfaceControlViewHostFactory { default SurfaceControlViewHost create(Context c, Display d, WindowlessWindowManager wmm) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModel.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModel.kt index 61963cde2d06..0e40a5350a43 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModel.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModel.kt @@ -103,7 +103,7 @@ class DesktopTilingDecorViewModel( fun moveTaskToFrontIfTiled(taskInfo: RunningTaskInfo): Boolean { return tilingTransitionHandlerByDisplayId .get(taskInfo.displayId) - ?.moveTiledPairToFront(taskInfo) ?: false + ?.moveTiledPairToFront(taskInfo, isTaskFocused = true) ?: false } fun onOverviewAnimationStateChange(isRunning: Boolean) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt index 1c593c0362ba..418b8ecd5534 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt @@ -230,14 +230,14 @@ class DesktopTilingWindowDecoration( ResizeTrigger.TILING_DIVIDER, motionEvent, leftTiledTask.taskInfo, - displayController + displayController, ) desktopModeEventLogger.logTaskResizingStarted( ResizeTrigger.TILING_DIVIDER, motionEvent, rightTiledTask.taskInfo, - displayController + displayController, ) } @@ -303,7 +303,7 @@ class DesktopTilingWindowDecoration( leftTiledTask.taskInfo, leftTiledTask.newBounds.height(), leftTiledTask.newBounds.width(), - displayController + displayController, ) desktopModeEventLogger.logTaskResizingEnded( @@ -312,7 +312,7 @@ class DesktopTilingWindowDecoration( rightTiledTask.taskInfo, rightTiledTask.newBounds.height(), rightTiledTask.newBounds.width(), - displayController + displayController, ) if (leftTiledTask.newBounds == leftTiledTask.bounds) { @@ -471,9 +471,9 @@ class DesktopTilingWindowDecoration( } } + // Only called if [taskInfo] relates to a focused task private fun isTilingFocusRemoved(taskInfo: RunningTaskInfo): Boolean { - return taskInfo.isFocused && - isTilingFocused && + return isTilingFocused && taskInfo.taskId != leftTaskResizingHelper?.taskInfo?.taskId && taskInfo.taskId != rightTaskResizingHelper?.taskInfo?.taskId } @@ -484,9 +484,9 @@ class DesktopTilingWindowDecoration( } } + // Only called if [taskInfo] relates to a focused task private fun isTilingRefocused(taskInfo: RunningTaskInfo): Boolean { return !isTilingFocused && - taskInfo.isFocused && (taskInfo.taskId == leftTaskResizingHelper?.taskInfo?.taskId || taskInfo.taskId == rightTaskResizingHelper?.taskInfo?.taskId) } @@ -573,9 +573,19 @@ class DesktopTilingWindowDecoration( removeTaskIfTiled(taskId, taskVanished = true, shouldDelayUpdate = true) } - fun moveTiledPairToFront(taskInfo: RunningTaskInfo): Boolean { + /** + * Moves the tiled pair to the front of the task stack, if the [taskInfo] is focused and one of + * the two tiled tasks. + * + * If specified, [isTaskFocused] will override [RunningTaskInfo.isFocused]. This is to be used + * when called when the task will be focused, but the [taskInfo] hasn't been updated yet. + */ + fun moveTiledPairToFront(taskInfo: RunningTaskInfo, isTaskFocused: Boolean? = null): Boolean { if (!isTilingManagerInitialised) return false + val isFocused = isTaskFocused ?: taskInfo.isFocused + if (!isFocused) return false + // If a task that isn't tiled is being focused, let the generic handler do the work. if (isTilingFocusRemoved(taskInfo)) { isTilingFocused = false diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt index b5700ffb046b..503ad92d4d71 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt @@ -34,15 +34,14 @@ import android.view.accessibility.AccessibilityEvent import android.view.accessibility.AccessibilityNodeInfo import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction import android.widget.ImageButton +import android.window.DesktopModeFlags import androidx.core.view.ViewCompat import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat import com.android.internal.policy.SystemBarUtils -import com.android.window.flags.Flags import com.android.wm.shell.R import com.android.wm.shell.shared.animation.Interpolators import com.android.wm.shell.windowdecor.WindowManagerWrapper import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer -import com.android.wm.shell.windowdecor.viewholder.WindowDecorationViewHolder.Data /** * A desktop mode window decoration used when the window is in full "focus" (i.e. fullscreen/split). @@ -141,7 +140,7 @@ internal class AppHandleViewHolder( private fun createStatusBarInputLayer(handlePosition: Point, handleWidth: Int, handleHeight: Int) { - if (!Flags.enableHandleInputFix()) return + if (!DesktopModeFlags.ENABLE_HANDLE_INPUT_FIX.isTrue()) return statusBarInputLayer = AdditionalSystemViewContainer(context, windowManagerWrapper, taskInfo.taskId, handlePosition.x, handlePosition.y, handleWidth, handleHeight, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt index 4fe66f3357a3..4cddf31321d6 100644 --- a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt @@ -23,8 +23,9 @@ import android.tools.flicker.assertors.assertions.AppLayerIncreasesInSize import android.tools.flicker.assertors.assertions.AppLayerIsInvisibleAtEnd import android.tools.flicker.assertors.assertions.AppLayerIsVisibleAlways import android.tools.flicker.assertors.assertions.AppLayerIsVisibleAtStart -import android.tools.flicker.assertors.assertions.AppWindowBecomesVisible import android.tools.flicker.assertors.assertions.AppWindowAlignsWithOnlyOneDisplayCornerAtEnd +import android.tools.flicker.assertors.assertions.AppWindowBecomesInvisible +import android.tools.flicker.assertors.assertions.AppWindowBecomesVisible import android.tools.flicker.assertors.assertions.AppWindowCoversLeftHalfScreenAtEnd import android.tools.flicker.assertors.assertions.AppWindowCoversRightHalfScreenAtEnd import android.tools.flicker.assertors.assertions.AppWindowHasDesktopModeInitialBoundsAtTheEnd @@ -44,6 +45,7 @@ import android.tools.flicker.assertors.assertions.LauncherWindowReplacesAppAsTop import android.tools.flicker.config.AssertionTemplates import android.tools.flicker.config.FlickerConfigEntry import android.tools.flicker.config.ScenarioId +import android.tools.flicker.config.common.Components.LAUNCHER import android.tools.flicker.config.desktopmode.Components.DESKTOP_MODE_APP import android.tools.flicker.config.desktopmode.Components.DESKTOP_WALLPAPER import android.tools.flicker.config.desktopmode.Components.NON_RESIZABLE_APP @@ -365,5 +367,57 @@ class DesktopModeFlickerScenarios { AppWindowAlignsWithOnlyOneDisplayCornerAtEnd(DESKTOP_MODE_APP) ).associateBy({ it }, { AssertionInvocationGroup.BLOCKING }), ) + + val MINIMIZE_APP = + FlickerConfigEntry( + scenarioId = ScenarioId("MINIMIZE_APP"), + extractor = + ShellTransitionScenarioExtractor( + transitionMatcher = + object : ITransitionMatcher { + override fun findAll( + transitions: Collection<Transition> + ): Collection<Transition> { + return transitions + .filter { it.type == TransitionType.MINIMIZE } + .sortedByDescending { it.id } + .drop(1) + } + } + ), + assertions = + AssertionTemplates.COMMON_ASSERTIONS + + listOf( + AppWindowOnTopAtStart(DESKTOP_MODE_APP), + AppWindowBecomesInvisible(DESKTOP_MODE_APP), + ).associateBy({ it }, { AssertionInvocationGroup.BLOCKING }) + ) + + val MINIMIZE_LAST_APP = + FlickerConfigEntry( + scenarioId = ScenarioId("MINIMIZE_LAST_APP"), + extractor = + ShellTransitionScenarioExtractor( + transitionMatcher = + object : ITransitionMatcher { + override fun findAll( + transitions: Collection<Transition> + ): Collection<Transition> { + val lastTransition = + transitions + .filter { it.type == TransitionType.MINIMIZE } + .maxByOrNull { it.id }!! + return listOf(lastTransition) + } + } + ), + assertions = + AssertionTemplates.COMMON_ASSERTIONS + + listOf( + AppWindowOnTopAtStart(DESKTOP_MODE_APP), + AppWindowBecomesInvisible(DESKTOP_MODE_APP), + AppWindowOnTopAtEnd(LAUNCHER), + ).associateBy({ it }, { AssertionInvocationGroup.BLOCKING }) + ) } } diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MinimizeAppsLandscape.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MinimizeAppsLandscape.kt new file mode 100644 index 000000000000..58582b02c212 --- /dev/null +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MinimizeAppsLandscape.kt @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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 + +import android.tools.Rotation.ROTATION_90 +import android.tools.flicker.FlickerConfig +import android.tools.flicker.annotation.ExpectedScenarios +import android.tools.flicker.annotation.FlickerConfigProvider +import android.tools.flicker.config.FlickerConfig +import android.tools.flicker.config.FlickerServiceConfig +import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner +import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.MINIMIZE_APP +import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.MINIMIZE_LAST_APP +import com.android.wm.shell.scenarios.MinimizeAppWindows +import org.junit.Test +import org.junit.runner.RunWith + +/** + * Minimize app windows by pressing the minimize button. + * + * Assert that the app windows gets hidden. + */ +@RunWith(FlickerServiceJUnit4ClassRunner::class) +class MinimizeAppsLandscape : MinimizeAppWindows(rotation = ROTATION_90) { + @ExpectedScenarios(["MINIMIZE_APP", "MINIMIZE_LAST_APP"]) + @Test + override fun minimizeAllAppWindows() = super.minimizeAllAppWindows() + + companion object { + @JvmStatic + @FlickerConfigProvider + fun flickerConfigProvider(): FlickerConfig = + FlickerConfig() + .use(FlickerServiceConfig.DEFAULT) + .use(MINIMIZE_APP) + .use(MINIMIZE_LAST_APP) + } +} diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MinimizeAppsPortrait.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MinimizeAppsPortrait.kt new file mode 100644 index 000000000000..7970426a6ee8 --- /dev/null +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/MinimizeAppsPortrait.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.wm.shell.flicker + +import android.tools.flicker.FlickerConfig +import android.tools.flicker.annotation.ExpectedScenarios +import android.tools.flicker.annotation.FlickerConfigProvider +import android.tools.flicker.config.FlickerConfig +import android.tools.flicker.config.FlickerServiceConfig +import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner +import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.MINIMIZE_APP +import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.MINIMIZE_LAST_APP +import com.android.wm.shell.scenarios.MinimizeAppWindows +import org.junit.Test +import org.junit.runner.RunWith + +/** + * Minimize app windows by pressing the minimize button. + * + * Assert that the app windows gets hidden. + */ +@RunWith(FlickerServiceJUnit4ClassRunner::class) +class MinimizeAppsPortrait : MinimizeAppWindows() { + @ExpectedScenarios(["MINIMIZE_APP", "MINIMIZE_LAST_APP"]) + @Test + override fun minimizeAllAppWindows() = super.minimizeAllAppWindows() + + companion object { + @JvmStatic + @FlickerConfigProvider + fun flickerConfigProvider(): FlickerConfig = + FlickerConfig() + .use(FlickerServiceConfig.DEFAULT) + .use(MINIMIZE_APP) + .use(MINIMIZE_LAST_APP) + } +} diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ExitDesktopWithDragToTopDragZone.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ExitDesktopWithDragToTopDragZone.kt index 824c4482c1e6..f442fdb31592 100644 --- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ExitDesktopWithDragToTopDragZone.kt +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ExitDesktopWithDragToTopDragZone.kt @@ -18,6 +18,7 @@ package com.android.wm.shell.scenarios import android.tools.NavBar import android.tools.Rotation +import com.android.internal.R import com.android.window.flags.Flags import com.android.wm.shell.Utils import org.junit.After @@ -40,6 +41,9 @@ constructor( @Before fun setup() { Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet) + // Skip the test when the drag-to-maximize is enabled on this device. + Assume.assumeFalse(Flags.enableDragToMaximize() && + instrumentation.context.resources.getBoolean(R.bool.config_dragToMaximizeInDesktopMode)) tapl.setEnableRotation(true) tapl.setExpectedRotation(rotation.value) testApp.enterDesktopWithDrag(wmHelper, device) diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleViewTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleViewTest.java index d38b848fbb4d..329a10998f23 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleViewTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleViewTest.java @@ -16,9 +16,8 @@ package com.android.wm.shell.bubbles.bar; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertFalse; -import android.graphics.drawable.ColorDrawable; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; @@ -47,10 +46,9 @@ public class BubbleBarHandleViewTest extends ShellTestCase { public void testUpdateHandleColor_lightBg() { mHandleView.updateHandleColor(false /* isRegionDark */, false /* animated */); - assertTrue(mHandleView.getClipToOutline()); - assertTrue(mHandleView.getBackground() instanceof ColorDrawable); - ColorDrawable bgDrawable = (ColorDrawable) mHandleView.getBackground(); - assertEquals(bgDrawable.getColor(), + assertFalse(mHandleView.getClipToOutline()); + int handleColor = mHandleView.mHandlePaint.getColor(); + assertEquals(handleColor, ContextCompat.getColor(mContext, R.color.bubble_bar_expanded_view_handle_dark)); } @@ -58,10 +56,9 @@ public class BubbleBarHandleViewTest extends ShellTestCase { public void testUpdateHandleColor_darkBg() { mHandleView.updateHandleColor(true /* isRegionDark */, false /* animated */); - assertTrue(mHandleView.getClipToOutline()); - assertTrue(mHandleView.getBackground() instanceof ColorDrawable); - ColorDrawable bgDrawable = (ColorDrawable) mHandleView.getBackground(); - assertEquals(bgDrawable.getColor(), + assertFalse(mHandleView.getClipToOutline()); + int handleColor = mHandleView.mHandlePaint.getColor(); + assertEquals(handleColor, ContextCompat.getColor(mContext, R.color.bubble_bar_expanded_view_handle_light)); } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipAppOpsListenerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipAppOpsListenerTest.java new file mode 100644 index 000000000000..b9490b881d08 --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipAppOpsListenerTest.java @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.pip; + +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.verify; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; + +import android.app.AppOpsManager; +import android.content.ComponentName; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; +import android.util.Pair; + +import androidx.test.filters.SmallTest; + +import com.android.wm.shell.common.ShellExecutor; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +/** + * Unit test against {@link PipAppOpsListener}. + */ +@SmallTest +@TestableLooper.RunWithLooper +@RunWith(AndroidTestingRunner.class) +public class PipAppOpsListenerTest { + + @Mock private Context mMockContext; + @Mock private PackageManager mMockPackageManager; + @Mock private AppOpsManager mMockAppOpsManager; + @Mock private PipAppOpsListener.Callback mMockCallback; + @Mock private ShellExecutor mMockExecutor; + + private PipAppOpsListener mPipAppOpsListener; + + private ArgumentCaptor<AppOpsManager.OnOpChangedListener> mOnOpChangedListenerCaptor; + private ArgumentCaptor<Runnable> mRunnableArgumentCaptor; + private Pair<ComponentName, Integer> mTopPipActivity; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); + when(mMockContext.getSystemService(Context.APP_OPS_SERVICE)) + .thenReturn(mMockAppOpsManager); + mOnOpChangedListenerCaptor = ArgumentCaptor.forClass( + AppOpsManager.OnOpChangedListener.class); + mRunnableArgumentCaptor = ArgumentCaptor.forClass(Runnable.class); + } + + @Test + public void onActivityPinned_registerAppOpsListener() { + String packageName = "com.android.test.pip"; + mPipAppOpsListener = new PipAppOpsListener(mMockContext, mMockCallback, mMockExecutor); + + mPipAppOpsListener.onActivityPinned(packageName); + + verify(mMockAppOpsManager).startWatchingMode( + eq(AppOpsManager.OP_PICTURE_IN_PICTURE), eq(packageName), + any(AppOpsManager.OnOpChangedListener.class)); + } + + @Test + public void onActivityUnpinned_unregisterAppOpsListener() { + mPipAppOpsListener = new PipAppOpsListener(mMockContext, mMockCallback, mMockExecutor); + + mPipAppOpsListener.onActivityUnpinned(); + + verify(mMockAppOpsManager).stopWatchingMode(any(AppOpsManager.OnOpChangedListener.class)); + } + + @Test + public void disablePipAppOps_dismissPip() throws PackageManager.NameNotFoundException { + String packageName = "com.android.test.pip"; + mPipAppOpsListener = new PipAppOpsListener(mMockContext, mMockCallback, mMockExecutor); + // Set up the top pip activity info as mTopPipActivity + mTopPipActivity = new Pair<>(new ComponentName(packageName, "PipActivity"), 0); + mPipAppOpsListener.setTopPipActivityInfoSupplier(this::getTopPipActivity); + // Set up the application info as mApplicationInfo + ApplicationInfo applicationInfo = new ApplicationInfo(); + applicationInfo.packageName = packageName; + when(mMockPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), anyInt())) + .thenReturn(applicationInfo); + // Mock the mode to be **not** allowed + when(mMockAppOpsManager.checkOpNoThrow(anyInt(), anyInt(), eq(packageName))) + .thenReturn(AppOpsManager.MODE_DEFAULT); + // Set up the initial state + mPipAppOpsListener.onActivityPinned(packageName); + verify(mMockAppOpsManager).startWatchingMode( + eq(AppOpsManager.OP_PICTURE_IN_PICTURE), eq(packageName), + mOnOpChangedListenerCaptor.capture()); + AppOpsManager.OnOpChangedListener opChangedListener = mOnOpChangedListenerCaptor.getValue(); + + opChangedListener.onOpChanged(String.valueOf(AppOpsManager.OP_PICTURE_IN_PICTURE), + packageName); + + verify(mMockExecutor).execute(mRunnableArgumentCaptor.capture()); + Runnable runnable = mRunnableArgumentCaptor.getValue(); + runnable.run(); + verify(mMockCallback).dismissPip(); + } + + @Test + public void disablePipAppOps_differentPackage_doNothing() + throws PackageManager.NameNotFoundException { + String packageName = "com.android.test.pip"; + mPipAppOpsListener = new PipAppOpsListener(mMockContext, mMockCallback, mMockExecutor); + // Set up the top pip activity info as mTopPipActivity + mTopPipActivity = new Pair<>(new ComponentName(packageName, "PipActivity"), 0); + mPipAppOpsListener.setTopPipActivityInfoSupplier(this::getTopPipActivity); + // Set up the application info as mApplicationInfo + ApplicationInfo applicationInfo = new ApplicationInfo(); + applicationInfo.packageName = packageName + ".modified"; + when(mMockPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), anyInt())) + .thenReturn(applicationInfo); + // Mock the mode to be **not** allowed + when(mMockAppOpsManager.checkOpNoThrow(anyInt(), anyInt(), eq(packageName))) + .thenReturn(AppOpsManager.MODE_DEFAULT); + // Set up the initial state + mPipAppOpsListener.onActivityPinned(packageName); + verify(mMockAppOpsManager).startWatchingMode( + eq(AppOpsManager.OP_PICTURE_IN_PICTURE), eq(packageName), + mOnOpChangedListenerCaptor.capture()); + AppOpsManager.OnOpChangedListener opChangedListener = mOnOpChangedListenerCaptor.getValue(); + + opChangedListener.onOpChanged(String.valueOf(AppOpsManager.OP_PICTURE_IN_PICTURE), + packageName); + + verifyZeroInteractions(mMockExecutor); + } + + @Test + public void disablePipAppOps_nameNotFound_unregisterAppOpsListener() + throws PackageManager.NameNotFoundException { + String packageName = "com.android.test.pip"; + mPipAppOpsListener = new PipAppOpsListener(mMockContext, mMockCallback, mMockExecutor); + // Set up the top pip activity info as mTopPipActivity + mTopPipActivity = new Pair<>(new ComponentName(packageName, "PipActivity"), 0); + mPipAppOpsListener.setTopPipActivityInfoSupplier(this::getTopPipActivity); + // Set up the application info as mApplicationInfo + ApplicationInfo applicationInfo = new ApplicationInfo(); + applicationInfo.packageName = packageName; + when(mMockPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), anyInt())) + .thenThrow(PackageManager.NameNotFoundException.class); + // Mock the mode to be **not** allowed + when(mMockAppOpsManager.checkOpNoThrow(anyInt(), anyInt(), eq(packageName))) + .thenReturn(AppOpsManager.MODE_DEFAULT); + // Set up the initial state + mPipAppOpsListener.onActivityPinned(packageName); + verify(mMockAppOpsManager).startWatchingMode( + eq(AppOpsManager.OP_PICTURE_IN_PICTURE), eq(packageName), + mOnOpChangedListenerCaptor.capture()); + AppOpsManager.OnOpChangedListener opChangedListener = mOnOpChangedListenerCaptor.getValue(); + + opChangedListener.onOpChanged(String.valueOf(AppOpsManager.OP_PICTURE_IN_PICTURE), + packageName); + + verify(mMockAppOpsManager).stopWatchingMode(any(AppOpsManager.OnOpChangedListener.class)); + } + + private Pair<ComponentName, Integer> getTopPipActivity(Context context) { + return mTopPipActivity; + } +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopBackNavigationTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopBackNavigationTransitionHandlerTest.kt new file mode 100644 index 000000000000..6df8d6fd7717 --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopBackNavigationTransitionHandlerTest.kt @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.desktopmode + +import android.app.ActivityManager.RunningTaskInfo +import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD +import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM +import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN +import android.app.WindowConfiguration.WindowingMode +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper.RunWithLooper +import android.view.SurfaceControl +import android.view.WindowManager +import android.view.WindowManager.TRANSIT_CLOSE +import android.window.TransitionInfo +import androidx.test.filters.SmallTest +import com.android.wm.shell.ShellTestCase +import com.android.wm.shell.TestRunningTaskInfoBuilder +import com.android.wm.shell.common.DisplayController +import com.android.wm.shell.common.ShellExecutor +import org.junit.Assert.assertFalse +import org.junit.Assert.assertNull +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.kotlin.any +import org.mockito.kotlin.mock +import org.mockito.kotlin.whenever + +@SmallTest +@RunWithLooper +@RunWith(AndroidTestingRunner::class) +class DesktopBackNavigationTransitionHandlerTest : ShellTestCase() { + + private val testExecutor = mock<ShellExecutor>() + private val closingTaskLeash = mock<SurfaceControl>() + private val displayController = mock<DisplayController>() + + private lateinit var handler: DesktopBackNavigationTransitionHandler + + @Before + fun setUp() { + handler = + DesktopBackNavigationTransitionHandler( + testExecutor, + testExecutor, + displayController + ) + whenever(displayController.getDisplayContext(any())).thenReturn(mContext) + } + + @Test + fun handleRequest_returnsNull() { + assertNull(handler.handleRequest(mock(), mock())) + } + + @Test + fun startAnimation_openTransition_returnsFalse() { + val animates = + handler.startAnimation( + transition = mock(), + info = + createTransitionInfo( + type = WindowManager.TRANSIT_OPEN, + task = createTask(WINDOWING_MODE_FREEFORM) + ), + startTransaction = mock(), + finishTransaction = mock(), + finishCallback = {} + ) + + assertFalse("Should not animate open transition", animates) + } + + @Test + fun startAnimation_toBackTransitionFullscreenTask_returnsFalse() { + val animates = + handler.startAnimation( + transition = mock(), + info = createTransitionInfo(task = createTask(WINDOWING_MODE_FULLSCREEN)), + startTransaction = mock(), + finishTransaction = mock(), + finishCallback = {} + ) + + assertFalse("Should not animate fullscreen task to back transition", animates) + } + + @Test + fun startAnimation_toBackTransitionOpeningFreeformTask_returnsFalse() { + val animates = + handler.startAnimation( + transition = mock(), + info = + createTransitionInfo( + changeMode = WindowManager.TRANSIT_OPEN, + task = createTask(WINDOWING_MODE_FREEFORM) + ), + startTransaction = mock(), + finishTransaction = mock(), + finishCallback = {} + ) + + assertFalse("Should not animate opening freeform task to back transition", animates) + } + + @Test + fun startAnimation_toBackTransitionToBackFreeformTask_returnsTrue() { + val animates = + handler.startAnimation( + transition = mock(), + info = createTransitionInfo(task = createTask(WINDOWING_MODE_FREEFORM)), + startTransaction = mock(), + finishTransaction = mock(), + finishCallback = {} + ) + + assertTrue("Should animate going to back freeform task close transition", animates) + } + + @Test + fun startAnimation_closeTransitionClosingFreeformTask_returnsTrue() { + val animates = + handler.startAnimation( + transition = mock(), + info = createTransitionInfo( + type = TRANSIT_CLOSE, + changeMode = TRANSIT_CLOSE, + task = createTask(WINDOWING_MODE_FREEFORM) + ), + startTransaction = mock(), + finishTransaction = mock(), + finishCallback = {} + ) + + assertTrue("Should animate going to back freeform task close transition", animates) + } + private fun createTransitionInfo( + type: Int = WindowManager.TRANSIT_TO_BACK, + changeMode: Int = WindowManager.TRANSIT_TO_BACK, + task: RunningTaskInfo + ): TransitionInfo = + TransitionInfo(type, 0 /* flags */).apply { + addChange( + TransitionInfo.Change(mock(), closingTaskLeash).apply { + mode = changeMode + parent = null + taskInfo = task + } + ) + } + + private fun createTask(@WindowingMode windowingMode: Int): RunningTaskInfo = + TestRunningTaskInfoBuilder() + .setActivityType(ACTIVITY_TYPE_STANDARD) + .setWindowingMode(windowingMode) + .build() +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopImmersiveControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopImmersiveControllerTest.kt index e05a0b54fcf4..a4f4d05d2079 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopImmersiveControllerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopImmersiveControllerTest.kt @@ -15,6 +15,7 @@ */ package com.android.wm.shell.desktopmode +import android.animation.AnimatorTestRule import android.app.ActivityManager.RunningTaskInfo import android.app.WindowConfiguration.WINDOW_CONFIG_BOUNDS import android.graphics.Rect @@ -24,6 +25,7 @@ import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import android.platform.test.flag.junit.SetFlagsRule import android.testing.AndroidTestingRunner +import android.testing.TestableLooper import android.view.Display.DEFAULT_DISPLAY import android.view.Surface import android.view.SurfaceControl @@ -43,6 +45,7 @@ import com.android.wm.shell.common.DisplayLayout import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFreeformTask import com.android.wm.shell.sysui.ShellInit import com.android.wm.shell.transition.Transitions +import com.android.wm.shell.util.StubTransaction import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Rule @@ -64,17 +67,19 @@ import org.mockito.kotlin.whenever * Usage: atest WMShellUnitTests:DesktopImmersiveControllerTest */ @SmallTest +@TestableLooper.RunWithLooper @RunWith(AndroidTestingRunner::class) class DesktopImmersiveControllerTest : ShellTestCase() { @JvmField @Rule val setFlagsRule = SetFlagsRule() + @JvmField @Rule val animatorTestRule = AnimatorTestRule(this) @Mock private lateinit var mockTransitions: Transitions private lateinit var desktopRepository: DesktopRepository @Mock private lateinit var mockDisplayController: DisplayController @Mock private lateinit var mockShellTaskOrganizer: ShellTaskOrganizer @Mock private lateinit var mockDisplayLayout: DisplayLayout - private val transactionSupplier = { SurfaceControl.Transaction() } + private val transactionSupplier = { StubTransaction() } private lateinit var controller: DesktopImmersiveController @@ -89,10 +94,12 @@ class DesktopImmersiveControllerTest : ShellTestCase() { (invocation.getArgument(0) as Rect).set(STABLE_BOUNDS) } controller = DesktopImmersiveController( + shellInit = mock(), transitions = mockTransitions, desktopRepository = desktopRepository, displayController = mockDisplayController, shellTaskOrganizer = mockShellTaskOrganizer, + shellCommandHandler = mock(), transactionSupplier = transactionSupplier, ) } @@ -672,6 +679,60 @@ class DesktopImmersiveControllerTest : ShellTestCase() { assertThat(controller.isImmersiveChange(transition, change)).isTrue() } + @Test + @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP) + fun externalAnimateResizeChange_doesNotCleanUpPendingTransitionState() { + val task = createFreeformTask() + val mockBinder = mock(IBinder::class.java) + whenever(mockTransitions.startTransition(eq(TRANSIT_CHANGE), any(), eq(controller))) + .thenReturn(mockBinder) + desktopRepository.setTaskInFullImmersiveState( + displayId = task.displayId, + taskId = task.taskId, + immersive = true + ) + + controller.moveTaskToNonImmersive(task) + + controller.animateResizeChange( + change = TransitionInfo.Change(task.token, SurfaceControl()).apply { + taskInfo = task + }, + startTransaction = StubTransaction(), + finishTransaction = StubTransaction(), + finishCallback = { } + ) + animatorTestRule.advanceTimeBy(DesktopImmersiveController.FULL_IMMERSIVE_ANIM_DURATION_MS) + + assertThat(controller.state).isNotNull() + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP) + fun startAnimation_missingChange_clearsState() { + val task = createFreeformTask() + val mockBinder = mock(IBinder::class.java) + whenever(mockTransitions.startTransition(eq(TRANSIT_CHANGE), any(), eq(controller))) + .thenReturn(mockBinder) + desktopRepository.setTaskInFullImmersiveState( + displayId = task.displayId, + taskId = task.taskId, + immersive = false + ) + + controller.moveTaskToImmersive(task) + + controller.startAnimation( + transition = mockBinder, + info = createTransitionInfo(changes = emptyList()), + startTransaction = StubTransaction(), + finishTransaction = StubTransaction(), + finishCallback = {} + ) + + assertThat(controller.state).isNull() + } + private fun createTransitionInfo( @TransitionType type: Int = TRANSIT_CHANGE, @TransitionFlags flags: Int = 0, diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt index b06c2dad4ffc..f21f26443748 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt @@ -32,6 +32,7 @@ import android.testing.TestableLooper.RunWithLooper import android.view.SurfaceControl import android.view.WindowManager import android.view.WindowManager.TRANSIT_OPEN +import android.view.WindowManager.TRANSIT_TO_BACK import android.view.WindowManager.TransitionType import android.window.TransitionInfo import android.window.WindowContainerTransaction @@ -77,16 +78,28 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() { @JvmField @Rule val setFlagsRule = SetFlagsRule() - @Mock lateinit var transitions: Transitions - @Mock lateinit var desktopRepository: DesktopRepository - @Mock lateinit var freeformTaskTransitionHandler: FreeformTaskTransitionHandler - @Mock lateinit var closeDesktopTaskTransitionHandler: CloseDesktopTaskTransitionHandler - @Mock lateinit var desktopImmersiveController: DesktopImmersiveController - @Mock lateinit var interactionJankMonitor: InteractionJankMonitor - @Mock lateinit var mockHandler: Handler - @Mock lateinit var closingTaskLeash: SurfaceControl - @Mock lateinit var shellInit: ShellInit - @Mock lateinit var rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer + @Mock + lateinit var transitions: Transitions + @Mock + lateinit var desktopRepository: DesktopRepository + @Mock + lateinit var freeformTaskTransitionHandler: FreeformTaskTransitionHandler + @Mock + lateinit var closeDesktopTaskTransitionHandler: CloseDesktopTaskTransitionHandler + @Mock + lateinit var desktopBackNavigationTransitionHandler: DesktopBackNavigationTransitionHandler + @Mock + lateinit var desktopImmersiveController: DesktopImmersiveController + @Mock + lateinit var interactionJankMonitor: InteractionJankMonitor + @Mock + lateinit var mockHandler: Handler + @Mock + lateinit var closingTaskLeash: SurfaceControl + @Mock + lateinit var shellInit: ShellInit + @Mock + lateinit var rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer private lateinit var mixedHandler: DesktopMixedTransitionHandler @@ -100,6 +113,7 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() { freeformTaskTransitionHandler, closeDesktopTaskTransitionHandler, desktopImmersiveController, + desktopBackNavigationTransitionHandler, interactionJankMonitor, mockHandler, shellInit, @@ -595,6 +609,87 @@ class DesktopMixedTransitionHandlerTest : ShellTestCase() { assertThat(mixedHandler.pendingMixedTransitions).isEmpty() } + @Test + @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION) + fun startAnimation_withMinimizingDesktopTask_callsBackNavigationHandler() { + val minimizingTask = createTask(WINDOWING_MODE_FREEFORM) + val transition = Binder() + whenever(desktopRepository.getExpandedTaskCount(any())).thenReturn(2) + whenever( + desktopBackNavigationTransitionHandler.startAnimation(any(), any(), any(), any(), any()) + ) + .thenReturn(true) + mixedHandler.addPendingMixedTransition( + PendingMixedTransition.Minimize( + transition = transition, + minimizingTask = minimizingTask.taskId, + isLastTask = false, + ) + ) + + val minimizingTaskChange = createChange(minimizingTask) + val started = mixedHandler.startAnimation( + transition = transition, + info = + createTransitionInfo( + TRANSIT_TO_BACK, + listOf(minimizingTaskChange) + ), + startTransaction = mock(), + finishTransaction = mock(), + finishCallback = {} + ) + + assertTrue("Should delegate animation to back navigation transition handler", started) + verify(desktopBackNavigationTransitionHandler) + .startAnimation( + eq(transition), + argThat { info -> info.changes.contains(minimizingTaskChange) }, + any(), any(), any()) + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION) + fun startAnimation_withMinimizingLastDesktopTask_dispatchesTransition() { + val minimizingTask = createTask(WINDOWING_MODE_FREEFORM) + val transition = Binder() + whenever(desktopRepository.getExpandedTaskCount(any())).thenReturn(2) + whenever( + desktopBackNavigationTransitionHandler.startAnimation(any(), any(), any(), any(), any()) + ) + .thenReturn(true) + mixedHandler.addPendingMixedTransition( + PendingMixedTransition.Minimize( + transition = transition, + minimizingTask = minimizingTask.taskId, + isLastTask = true, + ) + ) + + val minimizingTaskChange = createChange(minimizingTask) + mixedHandler.startAnimation( + transition = transition, + info = + createTransitionInfo( + TRANSIT_TO_BACK, + listOf(minimizingTaskChange) + ), + startTransaction = mock(), + finishTransaction = mock(), + finishCallback = {} + ) + + verify(transitions) + .dispatchTransition( + eq(transition), + argThat { info -> info.changes.contains(minimizingTaskChange) }, + any(), + any(), + any(), + eq(mixedHandler) + ) + } + private fun createTransitionInfo( type: Int = WindowManager.TRANSIT_CLOSE, changeMode: Int = WindowManager.TRANSIT_CLOSE, diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt index 414c1a658b95..7f790d574a7e 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt @@ -936,6 +936,28 @@ class DesktopRepositoryTest : ShellTestCase() { } @Test + fun saveBoundsBeforeMinimize_boundsSavedByTaskId() { + val taskId = 1 + val bounds = Rect(0, 0, 200, 200) + + repo.saveBoundsBeforeMinimize(taskId, bounds) + + assertThat(repo.removeBoundsBeforeMinimize(taskId)).isEqualTo(bounds) + } + + @Test + fun removeBoundsBeforeMinimize_returnsNullAfterBoundsRemoved() { + val taskId = 1 + val bounds = Rect(0, 0, 200, 200) + repo.saveBoundsBeforeMinimize(taskId, bounds) + repo.removeBoundsBeforeMinimize(taskId) + + val boundsBeforeMinimize = repo.removeBoundsBeforeMinimize(taskId) + + assertThat(boundsBeforeMinimize).isNull() + } + + @Test fun getExpandedTasksOrdered_returnsFreeformTasksInCorrectOrder() { repo.addTask(displayId = DEFAULT_DISPLAY, taskId = 3, isVisible = true) repo.addTask(displayId = DEFAULT_DISPLAY, taskId = 2, isVisible = true) 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 315a46fcbd7b..ad266ead774e 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 @@ -3038,6 +3038,21 @@ class DesktopTasksControllerTest : ShellTestCase() { // Assert bounds set to stable bounds val wct = getLatestToggleResizeDesktopTaskWct() assertThat(findBoundsChange(wct, task)).isEqualTo(STABLE_BOUNDS) + // Assert event is properly logged + verify(desktopModeEventLogger, times(1)).logTaskResizingStarted( + ResizeTrigger.DRAG_TO_TOP_RESIZE_TRIGGER, + motionEvent, + task, + displayController + ) + verify(desktopModeEventLogger, times(1)).logTaskResizingEnded( + ResizeTrigger.DRAG_TO_TOP_RESIZE_TRIGGER, + motionEvent, + task, + STABLE_BOUNDS.height(), + STABLE_BOUNDS.width(), + displayController + ) } @Test @@ -3082,6 +3097,13 @@ class DesktopTasksControllerTest : ShellTestCase() { eq(STABLE_BOUNDS), anyOrNull(), ) + // Assert no event is logged + verify(desktopModeEventLogger, never()).logTaskResizingStarted( + any(), any(), any(), any(), any() + ) + verify(desktopModeEventLogger, never()).logTaskResizingEnded( + any(), any(), any(), any(), any(), any(), any() + ) } @Test diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt index 01b69aed8465..456b50da095b 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt @@ -17,6 +17,7 @@ package com.android.wm.shell.desktopmode import android.app.ActivityManager.RunningTaskInfo +import android.graphics.Rect import android.os.Binder import android.os.Handler import android.platform.test.annotations.DisableFlags @@ -24,8 +25,10 @@ import android.platform.test.annotations.EnableFlags import android.platform.test.flag.junit.SetFlagsRule import android.testing.AndroidTestingRunner import android.view.Display.DEFAULT_DISPLAY +import android.view.SurfaceControl import android.view.WindowManager.TRANSIT_OPEN import android.view.WindowManager.TRANSIT_TO_BACK +import android.window.TransitionInfo import android.window.WindowContainerTransaction import android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REMOVE_TASK import android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER @@ -63,6 +66,7 @@ import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.Mockito.any +import org.mockito.Mockito.mock import org.mockito.Mockito.spy import org.mockito.Mockito.`when` import org.mockito.kotlin.eq @@ -235,6 +239,30 @@ class DesktopTasksLimiterTest : ShellTestCase() { } @Test + fun onTransitionReady_pendingTransition_changeTaskToBack_boundsSaved() { + val bounds = Rect(0, 0, 200, 200) + val transition = Binder() + val task = setUpFreeformTask() + desktopTasksLimiter.addPendingMinimizeChange( + transition, displayId = DEFAULT_DISPLAY, taskId = task.taskId) + + val change = TransitionInfo.Change(task.token, mock(SurfaceControl::class.java)).apply { + mode = TRANSIT_TO_BACK + taskInfo = task + setStartAbsBounds(bounds) + } + desktopTasksLimiter.getTransitionObserver().onTransitionReady( + transition, + TransitionInfo(TRANSIT_OPEN, TransitionInfo.FLAG_NONE).apply { addChange(change) }, + StubTransaction() /* startTransaction */, + StubTransaction() /* finishTransaction */) + + assertThat(desktopTaskRepo.isMinimizedTask(taskId = task.taskId)).isTrue() + assertThat(desktopTaskRepo.removeBoundsBeforeMinimize(taskId = task.taskId)).isEqualTo( + bounds) + } + + @Test fun onTransitionReady_transitionMergedFromPending_taskIsMinimized() { val mergedTransition = Binder() val newTransition = Binder() diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt index 737439ce3cfe..7f1c1db3207a 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt @@ -76,6 +76,7 @@ class DesktopTasksTransitionObserverTest { private val context = mock<Context>() private val shellTaskOrganizer = mock<ShellTaskOrganizer>() private val taskRepository = mock<DesktopRepository>() + private val mixedHandler = mock<DesktopMixedTransitionHandler>() private lateinit var transitionObserver: DesktopTasksTransitionObserver private lateinit var shellInit: ShellInit @@ -87,7 +88,7 @@ class DesktopTasksTransitionObserverTest { transitionObserver = DesktopTasksTransitionObserver( - context, taskRepository, transitions, shellTaskOrganizer, shellInit + context, taskRepository, transitions, shellTaskOrganizer, mixedHandler, shellInit ) } @@ -106,6 +107,7 @@ class DesktopTasksTransitionObserverTest { ) verify(taskRepository).minimizeTask(task.displayId, task.taskId) + verify(mixedHandler).addPendingMixedTransition(any()) } @Test diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java index 840126421c08..e40bbad7adda 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java @@ -20,6 +20,8 @@ import static android.content.ClipDescription.MIMETYPE_APPLICATION_SHORTCUT; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.DragEvent.ACTION_DRAG_STARTED; +import static com.android.wm.shell.draganddrop.DragTestUtils.createAppClipData; + import static org.junit.Assert.assertFalse; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doReturn; @@ -30,9 +32,7 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import android.content.ClipData; -import android.content.ClipDescription; import android.content.Context; -import android.content.Intent; import android.os.RemoteException; import android.view.Display; import android.view.DragEvent; @@ -128,7 +128,7 @@ public class DragAndDropControllerTest extends ShellTestCase { doReturn(display).when(dragLayout).getDisplay(); doReturn(DEFAULT_DISPLAY).when(display).getDisplayId(); - final ClipData clipData = createClipData(); + final ClipData clipData = createAppClipData(MIMETYPE_APPLICATION_SHORTCUT); final DragEvent event = mock(DragEvent.class); doReturn(ACTION_DRAG_STARTED).when(event).getAction(); doReturn(clipData).when(event).getClipData(); @@ -150,15 +150,4 @@ public class DragAndDropControllerTest extends ShellTestCase { mController.onDrag(dragLayout, event); verify(mDragAndDropListener, never()).onDragStarted(); } - - private ClipData createClipData() { - ClipDescription clipDescription = new ClipDescription(MIMETYPE_APPLICATION_SHORTCUT, - new String[] { MIMETYPE_APPLICATION_SHORTCUT }); - Intent i = new Intent(); - i.putExtra(Intent.EXTRA_PACKAGE_NAME, "pkg"); - i.putExtra(Intent.EXTRA_SHORTCUT_ID, "shortcutId"); - i.putExtra(Intent.EXTRA_USER, android.os.Process.myUserHandle()); - ClipData.Item item = new ClipData.Item(i); - return new ClipData(clipDescription, item); - } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragSessionTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragSessionTest.kt new file mode 100644 index 000000000000..3d59342f62d8 --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragSessionTest.kt @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.wm.shell.draganddrop + +import android.app.ActivityTaskManager +import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD +import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN +import android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW +import android.app.WindowConfiguration.WINDOWING_MODE_PINNED +import android.content.ClipDescription +import android.content.ClipDescription.EXTRA_HIDE_DRAG_SOURCE_TASK_ID +import android.os.PersistableBundle +import android.os.RemoteException +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.wm.shell.ShellTestCase +import com.android.wm.shell.common.DisplayLayout +import com.android.wm.shell.draganddrop.DragTestUtils.createTaskInfo +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.MockitoAnnotations +import org.mockito.kotlin.any +import org.mockito.kotlin.whenever + +/** + * Tests for DragSession. + * + * Usage: atest WMShellUnitTests:DragSessionTest + */ +@SmallTest +@RunWith(AndroidTestingRunner::class) +class DragSessionTest : ShellTestCase() { + @Mock + private lateinit var activityTaskManager: ActivityTaskManager + + @Mock + private lateinit var displayLayout: DisplayLayout + + @Before + @Throws(RemoteException::class) + fun setUp() { + MockitoAnnotations.initMocks(this) + } + + @Test + fun testGetRunningTask() { + // Set up running tasks + val runningTasks = listOf( + createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD), + ) + whenever(activityTaskManager.getTasks(any(), any())).thenReturn(runningTasks) + + // Simulate dragging an app + val data = DragTestUtils.createAppClipData(ClipDescription.MIMETYPE_APPLICATION_SHORTCUT) + + // Start a new drag session + val session = DragSession(activityTaskManager, displayLayout, data, 0) + session.updateRunningTask() + + assertThat(session.runningTaskInfo).isEqualTo(runningTasks.first()) + assertThat(session.runningTaskWinMode).isEqualTo(runningTasks.first().windowingMode) + assertThat(session.runningTaskActType).isEqualTo(runningTasks.first().activityType) + } + + @Test + fun testGetRunningTaskWithFloatingTasks() { + // Set up running tasks + val runningTasks = listOf( + createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD), + createTaskInfo(WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD), + createTaskInfo(WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, alwaysOnTop=true), + ) + whenever(activityTaskManager.getTasks(any(), any())).thenReturn(runningTasks) + + // Simulate dragging an app + val data = DragTestUtils.createAppClipData(ClipDescription.MIMETYPE_APPLICATION_SHORTCUT) + + // Start a new drag session + val session = DragSession(activityTaskManager, displayLayout, data, 0) + session.updateRunningTask() + + // Ensure that we find the first non-floating task + assertThat(session.runningTaskInfo).isEqualTo(runningTasks.first()) + assertThat(session.runningTaskWinMode).isEqualTo(runningTasks.first().windowingMode) + assertThat(session.runningTaskActType).isEqualTo(runningTasks.first().activityType) + } + + @Test + fun testHideDragSource_readDragFlag() { + // Set up running tasks + val runningTasks = listOf( + createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD), + createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD), + ) + whenever(activityTaskManager.getTasks(any(), any())).thenReturn(runningTasks) + + // Simulate dragging an app with hide-drag-source set for the second (top most) app + val data = DragTestUtils.createAppClipData(ClipDescription.MIMETYPE_APPLICATION_SHORTCUT) + data.description.extras = + PersistableBundle().apply { + putInt( + EXTRA_HIDE_DRAG_SOURCE_TASK_ID, + runningTasks.last().taskId + ) + } + + // Start a new drag session + val session = DragSession(activityTaskManager, displayLayout, data, 0) + session.updateRunningTask() + + assertThat(session.hideDragSourceTaskId).isEqualTo(runningTasks.last().taskId) + } +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragTestUtils.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragTestUtils.kt new file mode 100644 index 000000000000..1680d9b9a86c --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragTestUtils.kt @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.wm.shell.draganddrop + +import android.app.ActivityManager +import android.app.PendingIntent +import android.content.ClipData +import android.content.ClipDescription +import android.content.ComponentName +import android.content.Intent +import android.content.pm.ActivityInfo +import android.content.pm.ApplicationInfo +import android.os.Process +import java.util.Random +import org.mockito.Mockito + +/** + * Convenience methods for drag tests. + */ +object DragTestUtils { + /** + * Creates an app-based clip data that is by default resizeable. + */ + @JvmStatic + fun createAppClipData(mimeType: String): ClipData { + val clipDescription = ClipDescription(mimeType, arrayOf(mimeType)) + val i = Intent() + when (mimeType) { + ClipDescription.MIMETYPE_APPLICATION_SHORTCUT -> { + i.putExtra(Intent.EXTRA_PACKAGE_NAME, "package") + i.putExtra(Intent.EXTRA_SHORTCUT_ID, "shortcut_id") + } + + ClipDescription.MIMETYPE_APPLICATION_TASK -> i.putExtra(Intent.EXTRA_TASK_ID, 12345) + ClipDescription.MIMETYPE_APPLICATION_ACTIVITY -> { + val pi = Mockito.mock(PendingIntent::class.java) + Mockito.doReturn(Process.myUserHandle()).`when`(pi).creatorUserHandle + i.putExtra(ClipDescription.EXTRA_PENDING_INTENT, pi) + } + } + i.putExtra(Intent.EXTRA_USER, Process.myUserHandle()) + val item = ClipData.Item(i) + item.activityInfo = ActivityInfo() + item.activityInfo.applicationInfo = ApplicationInfo() + val data = ClipData(clipDescription, item) + setClipDataResizeable(data, true) + return data + } + + /** + * Creates an intent-based clip data that is by default resizeable. + */ + @JvmStatic + fun createIntentClipData(intent: PendingIntent): ClipData { + val clipDescription = ClipDescription( + "Intent", + arrayOf(ClipDescription.MIMETYPE_TEXT_INTENT) + ) + val item = ClipData.Item.Builder() + .setIntentSender(intent.intentSender) + .build() + item.activityInfo = ActivityInfo() + item.activityInfo.applicationInfo = ApplicationInfo() + val data = ClipData(clipDescription, item) + setClipDataResizeable(data, true) + return data + } + + /** + * Sets the given clip data to be resizeable. + */ + @JvmStatic + fun setClipDataResizeable(data: ClipData, resizeable: Boolean) { + data.getItemAt(0).activityInfo.resizeMode = if (resizeable) + ActivityInfo.RESIZE_MODE_RESIZEABLE + else + ActivityInfo.RESIZE_MODE_UNRESIZEABLE + } + + /** + * Creates a task info with the given params. + */ + @JvmStatic + fun createTaskInfo(winMode: Int, actType: Int): ActivityManager.RunningTaskInfo { + return createTaskInfo(winMode, actType, false) + } + + /** + * Creates a task info with the given params. + */ + @JvmStatic + fun createTaskInfo(winMode: Int, actType: Int, alwaysOnTop: Boolean = false): + ActivityManager.RunningTaskInfo { + val info = ActivityManager.RunningTaskInfo() + info.taskId = Random().nextInt() + info.configuration.windowConfiguration.activityType = actType + info.configuration.windowConfiguration.windowingMode = winMode + info.configuration.windowConfiguration.isAlwaysOnTop = alwaysOnTop + info.isVisible = true + info.isResizeable = true + info.baseActivity = ComponentName( + "com.android.wm.shell", + ".ActivityWithMode$winMode" + ) + info.baseIntent = Intent() + info.baseIntent.setComponent(info.baseActivity) + val activityInfo = ActivityInfo() + activityInfo.packageName = info.baseActivity!!.packageName + activityInfo.name = info.baseActivity!!.className + info.topActivityInfo = activityInfo + return info + } +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/SplitDragPolicyTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/SplitDragPolicyTest.java index eb74218b866a..2cfce6933e1b 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/SplitDragPolicyTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/SplitDragPolicyTest.java @@ -22,11 +22,12 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.content.ClipDescription.MIMETYPE_APPLICATION_ACTIVITY; import static android.content.ClipDescription.MIMETYPE_APPLICATION_SHORTCUT; import static android.content.ClipDescription.MIMETYPE_APPLICATION_TASK; -import static android.content.ClipDescription.MIMETYPE_TEXT_INTENT; - -import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; +import static com.android.wm.shell.draganddrop.DragTestUtils.createAppClipData; +import static com.android.wm.shell.draganddrop.DragTestUtils.createIntentClipData; +import static com.android.wm.shell.draganddrop.DragTestUtils.createTaskInfo; +import static com.android.wm.shell.draganddrop.DragTestUtils.setClipDataResizeable; 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; @@ -55,11 +56,7 @@ import android.app.ActivityManager; import android.app.ActivityTaskManager; import android.app.PendingIntent; import android.content.ClipData; -import android.content.ClipDescription; -import android.content.ComponentName; import android.content.Context; -import android.content.Intent; -import android.content.pm.ActivityInfo; import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Insets; @@ -176,74 +173,11 @@ public class SplitDragPolicyTest extends ShellTestCase { mMockitoSession.finishMocking(); } - /** - * Creates an app-based clip data that is by default resizeable. - */ - private ClipData createAppClipData(String mimeType) { - ClipDescription clipDescription = new ClipDescription(mimeType, new String[] { mimeType }); - Intent i = new Intent(); - switch (mimeType) { - case MIMETYPE_APPLICATION_SHORTCUT: - i.putExtra(Intent.EXTRA_PACKAGE_NAME, "package"); - i.putExtra(Intent.EXTRA_SHORTCUT_ID, "shortcut_id"); - break; - case MIMETYPE_APPLICATION_TASK: - i.putExtra(Intent.EXTRA_TASK_ID, 12345); - break; - case MIMETYPE_APPLICATION_ACTIVITY: - final PendingIntent pi = mock(PendingIntent.class); - doReturn(android.os.Process.myUserHandle()).when(pi).getCreatorUserHandle(); - i.putExtra(ClipDescription.EXTRA_PENDING_INTENT, pi); - break; - } - i.putExtra(Intent.EXTRA_USER, android.os.Process.myUserHandle()); - ClipData.Item item = new ClipData.Item(i); - item.setActivityInfo(new ActivityInfo()); - ClipData data = new ClipData(clipDescription, item); - setClipDataResizeable(data, true); - return data; - } - - /** - * Creates an intent-based clip data that is by default resizeable. - */ - private ClipData createIntentClipData(PendingIntent intent) { - ClipDescription clipDescription = new ClipDescription("Intent", - new String[] { MIMETYPE_TEXT_INTENT }); - ClipData.Item item = new ClipData.Item.Builder() - .setIntentSender(intent.getIntentSender()) - .build(); - ClipData data = new ClipData(clipDescription, item); - return data; - } - - private ActivityManager.RunningTaskInfo createTaskInfo(int winMode, int actType) { - ActivityManager.RunningTaskInfo info = new ActivityManager.RunningTaskInfo(); - info.configuration.windowConfiguration.setActivityType(actType); - info.configuration.windowConfiguration.setWindowingMode(winMode); - info.isResizeable = true; - info.baseActivity = new ComponentName(getInstrumentation().getContext(), - ".ActivityWithMode" + winMode); - info.baseIntent = new Intent(); - info.baseIntent.setComponent(info.baseActivity); - ActivityInfo activityInfo = new ActivityInfo(); - activityInfo.packageName = info.baseActivity.getPackageName(); - activityInfo.name = info.baseActivity.getClassName(); - info.topActivityInfo = activityInfo; - return info; - } - private void setRunningTask(ActivityManager.RunningTaskInfo task) { doReturn(Collections.singletonList(task)).when(mActivityTaskManager) .getTasks(anyInt(), anyBoolean()); } - private void setClipDataResizeable(ClipData data, boolean resizeable) { - data.getItemAt(0).getActivityInfo().resizeMode = resizeable - ? ActivityInfo.RESIZE_MODE_RESIZEABLE - : ActivityInfo.RESIZE_MODE_UNRESIZEABLE; - } - @Test public void testDragAppOverFullscreenHome_expectOnlyFullscreenTarget() { dragOverFullscreenHome_expectOnlyFullscreenTarget(mActivityClipData); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java index 72950a8dc139..6d37ed766aef 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java @@ -28,8 +28,11 @@ import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTI import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import android.app.AppCompatTaskInfo; import android.app.TaskInfo; import android.graphics.Rect; import android.testing.AndroidTestingRunner; @@ -75,6 +78,7 @@ public class PipAnimationControllerTest extends ShellTestCase { .setContainerLayer() .setName("FakeLeash") .build(); + mTaskInfo.appCompatTaskInfo = mock(AppCompatTaskInfo.class); } @Test @@ -93,7 +97,8 @@ public class PipAnimationControllerTest extends ShellTestCase { final Rect endValue1 = new Rect(100, 100, 200, 200); final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController .getAnimator(mTaskInfo, mLeash, baseValue, startValue, endValue1, null, - TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0); + TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0, + false /* alwaysAnimateTaskBounds */); assertEquals("Expect ANIM_TYPE_BOUNDS animation", animator.getAnimationType(), PipAnimationController.ANIM_TYPE_BOUNDS); @@ -107,14 +112,16 @@ public class PipAnimationControllerTest extends ShellTestCase { final Rect endValue2 = new Rect(200, 200, 300, 300); final PipAnimationController.PipTransitionAnimator oldAnimator = mPipAnimationController .getAnimator(mTaskInfo, mLeash, baseValue, startValue, endValue1, null, - TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0); + TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0, + false /* alwaysAnimateTaskBounds */); oldAnimator.setSurfaceControlTransactionFactory( MockSurfaceControlHelper::createMockSurfaceControlTransaction); oldAnimator.start(); final PipAnimationController.PipTransitionAnimator newAnimator = mPipAnimationController .getAnimator(mTaskInfo, mLeash, baseValue, startValue, endValue2, null, - TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0); + TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0, + false /* alwaysAnimateTaskBounds */); assertEquals("getAnimator with same type returns same animator", oldAnimator, newAnimator); @@ -145,7 +152,8 @@ public class PipAnimationControllerTest extends ShellTestCase { // Fullscreen to PiP. PipAnimationController.PipTransitionAnimator<?> animator = mPipAnimationController .getAnimator(mTaskInfo, mLeash, null, startBounds, endBounds, null, - TRANSITION_DIRECTION_LEAVE_PIP, 0, ROTATION_90); + TRANSITION_DIRECTION_LEAVE_PIP, 0, ROTATION_90, + false /* alwaysAnimateTaskBounds */); // Apply fraction 1 to compute the end value. animator.applySurfaceControlTransaction(mLeash, tx, 1); final Rect rotatedEndBounds = new Rect(endBounds); @@ -157,7 +165,8 @@ public class PipAnimationControllerTest extends ShellTestCase { startBounds.set(0, 0, 1000, 500); endBounds.set(200, 100, 400, 500); animator = mPipAnimationController.getAnimator(mTaskInfo, mLeash, startBounds, startBounds, - endBounds, null, TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_270); + endBounds, null, TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_270, + false /* alwaysAnimateTaskBounds */); animator.applySurfaceControlTransaction(mLeash, tx, 1); rotatedEndBounds.set(endBounds); rotateBounds(rotatedEndBounds, startBounds, ROTATION_270); @@ -166,6 +175,37 @@ public class PipAnimationControllerTest extends ShellTestCase { } @Test + public void pipTransitionAnimator_rotatedEndValue_overrideMainWindowFrame() { + final SurfaceControl.Transaction tx = createMockSurfaceControlTransaction(); + final Rect startBounds = new Rect(200, 700, 400, 800); + final Rect endBounds = new Rect(0, 0, 500, 1000); + mTaskInfo.topActivityMainWindowFrame = new Rect(0, 250, 1000, 500); + + // Fullscreen task to PiP. + PipAnimationController.PipTransitionAnimator<?> animator = mPipAnimationController + .getAnimator(mTaskInfo, mLeash, null, startBounds, endBounds, null, + TRANSITION_DIRECTION_LEAVE_PIP, 0, ROTATION_90, + false /* alwaysAnimateTaskBounds */); + // Apply fraction 1 to compute the end value. + animator.applySurfaceControlTransaction(mLeash, tx, 1); + + assertEquals("Expect use main window frame", mTaskInfo.topActivityMainWindowFrame, + animator.mCurrentValue); + + // PiP to fullscreen. + mTaskInfo.topActivityMainWindowFrame = new Rect(0, 250, 1000, 500); + startBounds.set(0, 0, 1000, 500); + endBounds.set(200, 100, 400, 500); + animator = mPipAnimationController.getAnimator(mTaskInfo, mLeash, startBounds, startBounds, + endBounds, null, TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_270, + false /* alwaysAnimateTaskBounds */); + animator.applySurfaceControlTransaction(mLeash, tx, 1); + + assertEquals("Expect use main window frame", mTaskInfo.topActivityMainWindowFrame, + animator.mCurrentValue); + } + + @Test @SuppressWarnings("unchecked") public void pipTransitionAnimator_updateEndValue() { final Rect baseValue = new Rect(0, 0, 100, 100); @@ -174,7 +214,8 @@ public class PipAnimationControllerTest extends ShellTestCase { final Rect endValue2 = new Rect(200, 200, 300, 300); final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController .getAnimator(mTaskInfo, mLeash, baseValue, startValue, endValue1, null, - TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0); + TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0, + false /* alwaysAnimateTaskBounds */); animator.updateEndValue(endValue2); @@ -188,7 +229,8 @@ public class PipAnimationControllerTest extends ShellTestCase { final Rect endValue = new Rect(100, 100, 200, 200); final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController .getAnimator(mTaskInfo, mLeash, baseValue, startValue, endValue, null, - TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0); + TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0, + false /* alwaysAnimateTaskBounds */); animator.setSurfaceControlTransactionFactory( MockSurfaceControlHelper::createMockSurfaceControlTransaction); @@ -207,4 +249,126 @@ public class PipAnimationControllerTest extends ShellTestCase { verify(mPipAnimationCallback).onPipAnimationEnd(eq(mTaskInfo), any(SurfaceControl.Transaction.class), eq(animator)); } + + @Test + public void pipTransitionAnimator_overrideMainWindowFrame() { + final Rect baseValue = new Rect(0, 0, 100, 100); + final Rect startValue = new Rect(0, 0, 100, 100); + final Rect endValue = new Rect(100, 100, 200, 200); + mTaskInfo.topActivityMainWindowFrame = new Rect(0, 50, 100, 100); + PipAnimationController.PipTransitionAnimator<?> animator = mPipAnimationController + .getAnimator(mTaskInfo, mLeash, baseValue, startValue, endValue, null, + TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0, + false /* alwaysAnimateTaskBounds */); + + assertEquals("Expect base value is overridden for in-PIP transition", + mTaskInfo.topActivityMainWindowFrame, animator.getBaseValue()); + assertEquals("Expect start value is overridden for in-PIP transition", + mTaskInfo.topActivityMainWindowFrame, animator.getStartValue()); + assertEquals("Expect end value is not overridden for in-PIP transition", + endValue, animator.getEndValue()); + + animator = mPipAnimationController.getAnimator(mTaskInfo, mLeash, baseValue, startValue, + endValue, null, TRANSITION_DIRECTION_LEAVE_PIP, 0, ROTATION_0, + false /* alwaysAnimateTaskBounds */); + + assertEquals("Expect base value is not overridden for leave-PIP transition", + baseValue, animator.getBaseValue()); + assertEquals("Expect start value is not overridden for leave-PIP transition", + startValue, animator.getStartValue()); + assertEquals("Expect end value is overridden for leave-PIP transition", + mTaskInfo.topActivityMainWindowFrame, animator.getEndValue()); + } + + @Test + public void pipTransitionAnimator_animateTaskBounds() { + final Rect baseValue = new Rect(0, 0, 100, 100); + final Rect startValue = new Rect(0, 0, 100, 100); + final Rect endValue = new Rect(100, 100, 200, 200); + mTaskInfo.topActivityMainWindowFrame = new Rect(0, 50, 100, 100); + PipAnimationController.PipTransitionAnimator<?> animator = mPipAnimationController + .getAnimator(mTaskInfo, mLeash, baseValue, startValue, endValue, null, + TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0, + true /* alwaysAnimateTaskBounds */); + + assertEquals("Expect base value is not overridden for in-PIP transition", + baseValue, animator.getBaseValue()); + assertEquals("Expect start value is not overridden for in-PIP transition", + startValue, animator.getStartValue()); + assertEquals("Expect end value is not overridden for in-PIP transition", + endValue, animator.getEndValue()); + + animator = mPipAnimationController.getAnimator(mTaskInfo, mLeash, baseValue, startValue, + endValue, null, TRANSITION_DIRECTION_LEAVE_PIP, 0, ROTATION_0, + true /* alwaysAnimateTaskBounds */); + + assertEquals("Expect base value is not overridden for leave-PIP transition", + baseValue, animator.getBaseValue()); + assertEquals("Expect start value is not overridden for leave-PIP transition", + startValue, animator.getStartValue()); + assertEquals("Expect end value is not overridden for leave-PIP transition", + endValue, animator.getEndValue()); + } + + @Test + public void pipTransitionAnimator_letterboxed_animateTaskBounds() { + final Rect baseValue = new Rect(0, 0, 100, 100); + final Rect startValue = new Rect(0, 0, 100, 100); + final Rect endValue = new Rect(100, 100, 200, 200); + mTaskInfo.topActivityMainWindowFrame = new Rect(0, 50, 100, 100); + doReturn(true).when(mTaskInfo.appCompatTaskInfo).isTopActivityLetterboxed(); + PipAnimationController.PipTransitionAnimator<?> animator = mPipAnimationController + .getAnimator(mTaskInfo, mLeash, baseValue, startValue, endValue, null, + TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0, + false /* alwaysAnimateTaskBounds */); + + assertEquals("Expect base value is not overridden for in-PIP transition", + baseValue, animator.getBaseValue()); + assertEquals("Expect start value is not overridden for in-PIP transition", + startValue, animator.getStartValue()); + assertEquals("Expect end value is not overridden for in-PIP transition", + endValue, animator.getEndValue()); + + animator = mPipAnimationController.getAnimator(mTaskInfo, mLeash, baseValue, startValue, + endValue, null, TRANSITION_DIRECTION_LEAVE_PIP, 0, ROTATION_0, + false /* alwaysAnimateTaskBounds */); + + assertEquals("Expect base value is not overridden for leave-PIP transition", + baseValue, animator.getBaseValue()); + assertEquals("Expect start value is not overridden for leave-PIP transition", + startValue, animator.getStartValue()); + assertEquals("Expect end value is not overridden for leave-PIP transition", + endValue, animator.getEndValue()); + } + + @Test + public void pipTransitionAnimator_sizeCompat_animateTaskBounds() { + final Rect baseValue = new Rect(0, 0, 100, 100); + final Rect startValue = new Rect(0, 0, 100, 100); + final Rect endValue = new Rect(100, 100, 200, 200); + mTaskInfo.topActivityMainWindowFrame = new Rect(0, 50, 100, 100); + doReturn(true).when(mTaskInfo.appCompatTaskInfo).isTopActivityInSizeCompat(); + PipAnimationController.PipTransitionAnimator<?> animator = mPipAnimationController + .getAnimator(mTaskInfo, mLeash, baseValue, startValue, endValue, null, + TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0, + false /* alwaysAnimateTaskBounds */); + + assertEquals("Expect base value is not overridden for in-PIP transition", + baseValue, animator.getBaseValue()); + assertEquals("Expect start value is not overridden for in-PIP transition", + startValue, animator.getStartValue()); + assertEquals("Expect end value is not overridden for in-PIP transition", + endValue, animator.getEndValue()); + + animator = mPipAnimationController.getAnimator(mTaskInfo, mLeash, baseValue, startValue, + endValue, null, TRANSITION_DIRECTION_LEAVE_PIP, 0, ROTATION_0, + false /* alwaysAnimateTaskBounds */); + + assertEquals("Expect base value is not overridden for leave-PIP transition", + baseValue, animator.getBaseValue()); + assertEquals("Expect start value is not overridden for leave-PIP transition", + startValue, animator.getStartValue()); + assertEquals("Expect end value is not overridden for leave-PIP transition", + endValue, animator.getEndValue()); + } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java index bcb7461bfae7..5f58265b45f5 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java @@ -47,6 +47,7 @@ import android.window.WindowContainerToken; import androidx.test.filters.SmallTest; import com.android.wm.shell.MockSurfaceControlHelper; +import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.TestShellExecutor; @@ -61,6 +62,7 @@ import com.android.wm.shell.common.pip.PipKeepClearAlgorithmInterface; import com.android.wm.shell.common.pip.PipSnapAlgorithm; import com.android.wm.shell.common.pip.PipUiEventLogger; import com.android.wm.shell.common.pip.SizeSpecSource; +import com.android.wm.shell.desktopmode.DesktopRepository; import com.android.wm.shell.pip.phone.PhonePipMenuController; import com.android.wm.shell.splitscreen.SplitScreenController; @@ -90,6 +92,8 @@ public class PipTaskOrganizerTest extends ShellTestCase { @Mock private PipSurfaceTransactionHelper mMockPipSurfaceTransactionHelper; @Mock private PipUiEventLogger mMockPipUiEventLogger; @Mock private Optional<SplitScreenController> mMockOptionalSplitScreen; + @Mock private Optional<DesktopRepository> mMockOptionalDesktopRepository; + @Mock private RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer; @Mock private ShellTaskOrganizer mMockShellTaskOrganizer; @Mock private PipParamsChangedForwarder mMockPipParamsChangedForwarder; private TestShellExecutor mMainExecutor; @@ -120,8 +124,10 @@ public class PipTaskOrganizerTest extends ShellTestCase { mPipBoundsAlgorithm, mMockPhonePipMenuController, mMockPipAnimationController, mMockPipSurfaceTransactionHelper, mMockPipTransitionController, mMockPipParamsChangedForwarder, mMockOptionalSplitScreen, - Optional.empty() /* pipPerfHintControllerOptional */, mMockDisplayController, - mMockPipUiEventLogger, mMockShellTaskOrganizer, mMainExecutor); + Optional.empty() /* pipPerfHintControllerOptional */, + mMockOptionalDesktopRepository, mRootTaskDisplayAreaOrganizer, + mMockDisplayController, mMockPipUiEventLogger, mMockShellTaskOrganizer, + mMainExecutor); mMainExecutor.flushAll(); preparePipTaskOrg(); preparePipSurfaceTransactionHelper(); 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 new file mode 100644 index 000000000000..cab625216236 --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipSchedulerTest.java @@ -0,0 +1,270 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.pip2.phone; + +import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP; +import static com.android.wm.shell.transition.Transitions.TRANSIT_REMOVE_PIP; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.when; +import static org.mockito.kotlin.MatchersKt.eq; +import static org.mockito.kotlin.VerificationKt.times; +import static org.mockito.kotlin.VerificationKt.verify; + +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Matrix; +import android.graphics.Rect; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; +import android.view.SurfaceControl; +import android.window.WindowContainerToken; +import android.window.WindowContainerTransaction; + +import androidx.test.filters.SmallTest; + +import com.android.wm.shell.common.ShellExecutor; +import com.android.wm.shell.common.pip.PipBoundsState; +import com.android.wm.shell.pip.PipTransitionController; +import com.android.wm.shell.pip2.PipSurfaceTransactionHelper; +import com.android.wm.shell.pip2.animation.PipAlphaAnimator; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +/** + * Unit test against {@link PipScheduler} + */ + +@SmallTest +@TestableLooper.RunWithLooper +@RunWith(AndroidTestingRunner.class) +public class PipSchedulerTest { + private static final int TEST_RESIZE_DURATION = 1; + private static final Rect TEST_STARTING_BOUNDS = new Rect(0, 0, 10, 10); + private static final Rect TEST_BOUNDS = new Rect(0, 0, 20, 20); + + @Mock private Context mMockContext; + @Mock private Resources mMockResources; + @Mock private PipBoundsState mMockPipBoundsState; + @Mock private ShellExecutor mMockMainExecutor; + @Mock private PipTransitionState mMockPipTransitionState; + @Mock private PipTransitionController mMockPipTransitionController; + @Mock private Runnable mMockUpdateMovementBoundsRunnable; + @Mock private WindowContainerToken mMockPipTaskToken; + @Mock private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory mMockFactory; + @Mock private SurfaceControl.Transaction mMockTransaction; + @Mock private PipAlphaAnimator mMockAlphaAnimator; + + @Captor private ArgumentCaptor<Runnable> mRunnableArgumentCaptor; + @Captor private ArgumentCaptor<WindowContainerTransaction> mWctArgumentCaptor; + + private PipScheduler mPipScheduler; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + when(mMockContext.getResources()).thenReturn(mMockResources); + when(mMockResources.getInteger(anyInt())).thenReturn(0); + when(mMockPipBoundsState.getBounds()).thenReturn(TEST_STARTING_BOUNDS); + when(mMockFactory.getTransaction()).thenReturn(mMockTransaction); + when(mMockTransaction.setMatrix(any(SurfaceControl.class), any(Matrix.class), any())) + .thenReturn(mMockTransaction); + + mPipScheduler = new PipScheduler(mMockContext, mMockPipBoundsState, mMockMainExecutor, + mMockPipTransitionState); + mPipScheduler.setPipTransitionController(mMockPipTransitionController); + mPipScheduler.setSurfaceControlTransactionFactory(mMockFactory); + mPipScheduler.setPipAlphaAnimatorSupplier((context, leash, tx, direction) -> + mMockAlphaAnimator); + + SurfaceControl testLeash = new SurfaceControl.Builder() + .setContainerLayer() + .setName("PipSchedulerTest") + .setCallsite("PipSchedulerTest") + .build(); + when(mMockPipTransitionState.getPinnedTaskLeash()).thenReturn(testLeash); + } + + @Test + public void scheduleExitPipViaExpand_nullTaskToken_noop() { + setNullPipTaskToken(); + + mPipScheduler.scheduleExitPipViaExpand(); + + verify(mMockMainExecutor, never()).execute(any()); + } + + @Test + public void scheduleExitPipViaExpand_exitTransitionCalled() { + setMockPipTaskToken(); + + mPipScheduler.scheduleExitPipViaExpand(); + + verify(mMockMainExecutor, times(1)).execute(mRunnableArgumentCaptor.capture()); + assertNotNull(mRunnableArgumentCaptor.getValue()); + mRunnableArgumentCaptor.getValue().run(); + + verify(mMockPipTransitionController, times(1)) + .startExitTransition(eq(TRANSIT_EXIT_PIP), any(), isNull()); + } + + @Test + public void removePipAfterAnimation() { + //TODO: Update once this is changed to run animation as part of transition + setMockPipTaskToken(); + + mPipScheduler.removePipAfterAnimation(); + verify(mMockAlphaAnimator, times(1)) + .setAnimationEndCallback(mRunnableArgumentCaptor.capture()); + assertNotNull(mRunnableArgumentCaptor.getValue()); + verify(mMockAlphaAnimator, times(1)).start(); + + mRunnableArgumentCaptor.getValue().run(); + + verify(mMockMainExecutor, times(1)).execute(mRunnableArgumentCaptor.capture()); + assertNotNull(mRunnableArgumentCaptor.getValue()); + + mRunnableArgumentCaptor.getValue().run(); + + verify(mMockPipTransitionController, times(1)) + .startExitTransition(eq(TRANSIT_REMOVE_PIP), any(), isNull()); + } + + @Test + public void scheduleAnimateResizePip_bounds_nullTaskToken_noop() { + setNullPipTaskToken(); + + mPipScheduler.scheduleAnimateResizePip(TEST_BOUNDS); + + verify(mMockPipTransitionController, never()).startResizeTransition(any(), anyInt()); + } + + @Test + public void scheduleAnimateResizePip_boundsConfig_nullTaskToken_noop() { + setNullPipTaskToken(); + + mPipScheduler.scheduleAnimateResizePip(TEST_BOUNDS, true); + + verify(mMockPipTransitionController, never()).startResizeTransition(any(), anyInt()); + } + + @Test + public void scheduleAnimateResizePip_boundsConfig_setsConfigAtEnd() { + setMockPipTaskToken(); + when(mMockPipTransitionState.isInPip()).thenReturn(true); + + mPipScheduler.scheduleAnimateResizePip(TEST_BOUNDS, true); + + verify(mMockPipTransitionController, times(1)) + .startResizeTransition(mWctArgumentCaptor.capture(), anyInt()); + assertNotNull(mWctArgumentCaptor.getValue()); + assertNotNull(mWctArgumentCaptor.getValue().getChanges()); + boolean hasConfigAtEndChange = false; + for (WindowContainerTransaction.Change change : + mWctArgumentCaptor.getValue().getChanges().values()) { + if (change.getConfigAtTransitionEnd()) { + hasConfigAtEndChange = true; + break; + } + } + assertTrue(hasConfigAtEndChange); + } + + @Test + public void scheduleAnimateResizePip_boundsConfigDuration_nullTaskToken_noop() { + setNullPipTaskToken(); + + mPipScheduler.scheduleAnimateResizePip(TEST_BOUNDS, true, TEST_RESIZE_DURATION); + + verify(mMockPipTransitionController, never()).startResizeTransition(any(), anyInt()); + } + + @Test + public void scheduleAnimateResizePip_notInPip_noop() { + setMockPipTaskToken(); + when(mMockPipTransitionState.isInPip()).thenReturn(false); + + mPipScheduler.scheduleAnimateResizePip(TEST_BOUNDS, true, TEST_RESIZE_DURATION); + + verify(mMockPipTransitionController, never()).startResizeTransition(any(), anyInt()); + } + + @Test + public void scheduleAnimateResizePip_resizeTransition() { + setMockPipTaskToken(); + when(mMockPipTransitionState.isInPip()).thenReturn(true); + + mPipScheduler.scheduleAnimateResizePip(TEST_BOUNDS, true, TEST_RESIZE_DURATION); + + verify(mMockPipTransitionController, times(1)) + .startResizeTransition(any(), eq(TEST_RESIZE_DURATION)); + } + + @Test + public void scheduleUserResizePip_emptyBounds_noop() { + setMockPipTaskToken(); + + mPipScheduler.scheduleUserResizePip(new Rect()); + + verify(mMockTransaction, never()).apply(); + } + + @Test + public void scheduleUserResizePip_rotation_emptyBounds_noop() { + setMockPipTaskToken(); + + mPipScheduler.scheduleUserResizePip(new Rect(), 90); + + verify(mMockTransaction, never()).apply(); + } + + @Test + public void scheduleUserResizePip_applyTransaction() { + setMockPipTaskToken(); + + mPipScheduler.scheduleUserResizePip(TEST_BOUNDS, 90); + + verify(mMockTransaction, times(1)).apply(); + } + + @Test + public void finishResize_movementBoundsRunnableCalled() { + mPipScheduler.setUpdateMovementBoundsRunnable(mMockUpdateMovementBoundsRunnable); + mPipScheduler.scheduleFinishResizePip(TEST_BOUNDS); + + verify(mMockUpdateMovementBoundsRunnable, times(1)).run(); + } + + private void setNullPipTaskToken() { + when(mMockPipTransitionState.getPipTaskToken()).thenReturn(null); + } + + private void setMockPipTaskToken() { + when(mMockPipTransitionState.getPipTaskToken()).thenReturn(mMockPipTaskToken); + } +} 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 5ebf5170bf86..59141ca39487 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 @@ -19,6 +19,7 @@ package com.android.wm.shell.windowdecor import android.app.ActivityManager import android.app.WindowConfiguration import android.content.ComponentName +import android.graphics.Region import android.testing.AndroidTestingRunner import android.view.Display import android.view.InsetsState @@ -33,6 +34,9 @@ import org.junit.runner.RunWith @SmallTest @RunWith(AndroidTestingRunner::class) class CaptionWindowDecorationTests : ShellTestCase() { + + private val exclusionRegion = Region.obtain() + @Test fun updateRelayoutParams_freeformAndTransparentAppearance_allowsInputFallthrough() { val taskInfo = createTaskInfo() @@ -50,7 +54,8 @@ class CaptionWindowDecorationTests : ShellTestCase() { true /* isStatusBarVisible */, false /* isKeyguardVisibleAndOccluded */, InsetsState(), - true /* hasGlobalFocus */ + true /* hasGlobalFocus */, + exclusionRegion ) Truth.assertThat(relayoutParams.hasInputFeatureSpy()).isTrue() @@ -72,7 +77,8 @@ class CaptionWindowDecorationTests : ShellTestCase() { true /* isStatusBarVisible */, false /* isKeyguardVisibleAndOccluded */, InsetsState(), - true /* hasGlobalFocus */ + true /* hasGlobalFocus */, + exclusionRegion ) Truth.assertThat(relayoutParams.hasInputFeatureSpy()).isFalse() @@ -90,7 +96,8 @@ class CaptionWindowDecorationTests : ShellTestCase() { true /* isStatusBarVisible */, false /* isKeyguardVisibleAndOccluded */, InsetsState(), - true /* hasGlobalFocus */ + true /* hasGlobalFocus */, + exclusionRegion ) Truth.assertThat(relayoutParams.mOccludingCaptionElements.size).isEqualTo(2) Truth.assertThat(relayoutParams.mOccludingCaptionElements[0].mAlignment).isEqualTo( 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 956100d9bc03..be664f86e9f5 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 @@ -30,6 +30,7 @@ import android.content.Intent import android.content.Intent.ACTION_MAIN import android.content.pm.ActivityInfo import android.graphics.Rect +import android.graphics.Region import android.hardware.display.DisplayManager import android.hardware.display.VirtualDisplay import android.hardware.input.InputManager @@ -48,6 +49,7 @@ import android.testing.TestableLooper.RunWithLooper import android.util.SparseArray import android.view.Choreographer import android.view.Display.DEFAULT_DISPLAY +import android.view.ISystemGestureExclusionListener import android.view.IWindowManager import android.view.InputChannel import android.view.InputMonitor @@ -84,7 +86,6 @@ import com.android.wm.shell.common.DisplayController import com.android.wm.shell.common.DisplayInsetsController import com.android.wm.shell.common.DisplayLayout import com.android.wm.shell.common.MultiInstanceHelper -import com.android.wm.shell.common.ShellExecutor import com.android.wm.shell.common.SyncTransactionQueue import com.android.wm.shell.desktopmode.DesktopActivityOrientationChangeHandler import com.android.wm.shell.desktopmode.DesktopModeEventLogger @@ -131,6 +132,7 @@ import org.mockito.Mockito.times import org.mockito.kotlin.KArgumentCaptor import org.mockito.kotlin.verify import org.mockito.kotlin.any +import org.mockito.kotlin.anyOrNull import org.mockito.kotlin.argThat import org.mockito.kotlin.argumentCaptor import org.mockito.kotlin.doNothing @@ -175,7 +177,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { @Mock private lateinit var mockInputMonitorFactory: DesktopModeWindowDecorViewModel.InputMonitorFactory @Mock private lateinit var mockShellController: ShellController - @Mock private lateinit var mockShellExecutor: ShellExecutor + private val testShellExecutor = TestShellExecutor() @Mock private lateinit var mockAppHeaderViewHolderFactory: AppHeaderViewHolder.Factory @Mock private lateinit var mockRootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer @Mock private lateinit var mockShellCommandHandler: ShellCommandHandler @@ -230,13 +232,13 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { spyContext = spy(mContext) doNothing().`when`(spyContext).startActivity(any()) - shellInit = ShellInit(mockShellExecutor) + shellInit = ShellInit(testShellExecutor) windowDecorByTaskIdSpy.clear() spyContext.addMockSystemService(InputManager::class.java, mockInputManager) desktopModeEventLogger = mock<DesktopModeEventLogger>() desktopModeWindowDecorViewModel = DesktopModeWindowDecorViewModel( spyContext, - mockShellExecutor, + testShellExecutor, mockMainHandler, mockMainChoreographer, bgExecutor, @@ -1321,11 +1323,11 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { decoration.mHasGlobalFocus = true desktopModeWindowDecorViewModel.onTaskInfoChanged(task) - verify(decoration).relayout(task, true) + verify(decoration).relayout(eq(task), eq(true), anyOrNull()) decoration.mHasGlobalFocus = false desktopModeWindowDecorViewModel.onTaskInfoChanged(task) - verify(decoration).relayout(task, false) + verify(decoration).relayout(eq(task), eq(false), anyOrNull()) } @Test @@ -1342,17 +1344,66 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { task.isFocused = true desktopModeWindowDecorViewModel.onTaskInfoChanged(task) - verify(decoration).relayout(task, true) + verify(decoration).relayout(eq(task), eq(true), anyOrNull()) task.isFocused = false desktopModeWindowDecorViewModel.onTaskInfoChanged(task) - verify(decoration).relayout(task, false) + verify(decoration).relayout(eq(task), eq(false), anyOrNull()) + } + + @Test + fun testGestureExclusionChanged_updatesDecorations() { + val captor = argumentCaptor<ISystemGestureExclusionListener>() + verify(mockWindowManager) + .registerSystemGestureExclusionListener(captor.capture(), eq(DEFAULT_DISPLAY)) + val task = createOpenTaskDecoration( + windowingMode = WINDOWING_MODE_FREEFORM, + displayId = DEFAULT_DISPLAY + ) + val task2 = createOpenTaskDecoration( + windowingMode = WINDOWING_MODE_FREEFORM, + displayId = DEFAULT_DISPLAY + ) + val newRegion = Region.obtain().apply { + set(Rect(0, 0, 1600, 80)) + } + + captor.firstValue.onSystemGestureExclusionChanged(DEFAULT_DISPLAY, newRegion, newRegion) + testShellExecutor.flushAll() + + verify(task).onExclusionRegionChanged(newRegion) + verify(task2).onExclusionRegionChanged(newRegion) + } + + @Test + fun testGestureExclusionChanged_otherDisplay_skipsDecorationUpdate() { + val captor = argumentCaptor<ISystemGestureExclusionListener>() + verify(mockWindowManager) + .registerSystemGestureExclusionListener(captor.capture(), eq(DEFAULT_DISPLAY)) + val task = createOpenTaskDecoration( + windowingMode = WINDOWING_MODE_FREEFORM, + displayId = DEFAULT_DISPLAY + ) + val task2 = createOpenTaskDecoration( + windowingMode = WINDOWING_MODE_FREEFORM, + displayId = 2 + ) + val newRegion = Region.obtain().apply { + set(Rect(0, 0, 1600, 80)) + } + + captor.firstValue.onSystemGestureExclusionChanged(DEFAULT_DISPLAY, newRegion, newRegion) + testShellExecutor.flushAll() + + verify(task).onExclusionRegionChanged(newRegion) + verify(task2, never()).onExclusionRegionChanged(newRegion) } private fun createOpenTaskDecoration( @WindowingMode windowingMode: Int, taskSurface: SurfaceControl = SurfaceControl(), requestingImmersive: Boolean = false, + displayId: Int = DEFAULT_DISPLAY, onMaxOrRestoreListenerCaptor: ArgumentCaptor<Function0<Unit>> = forClass(Function0::class.java) as ArgumentCaptor<Function0<Unit>>, onImmersiveOrRestoreListenerCaptor: KArgumentCaptor<() -> Unit> = @@ -1376,6 +1427,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { ): DesktopModeWindowDecoration { val decor = setUpMockDecorationForTask(createTask( windowingMode = windowingMode, + displayId = displayId, requestingImmersive = requestingImmersive )) onTaskOpening(decor.mTaskInfo, taskSurface) 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 41f57ae0fd97..1d2d0f078817 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 @@ -64,6 +64,7 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.PointF; import android.graphics.Rect; +import android.graphics.Region; import android.net.Uri; import android.os.Handler; import android.os.SystemProperties; @@ -224,6 +225,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { private TestableContext mTestableContext; private final ShellExecutor mBgExecutor = new TestShellExecutor(); private final AssistContent mAssistContent = new AssistContent(); + private final Region mExclusionRegion = Region.obtain(); /** Set up run before test class. */ @BeforeClass @@ -262,8 +264,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { doReturn(defaultDisplay).when(mMockDisplayController).getDisplay(Display.DEFAULT_DISPLAY); doReturn(mInsetsState).when(mMockDisplayController).getInsetsState(anyInt()); when(mMockHandleMenuFactory.create(any(), any(), anyInt(), any(), any(), any(), - anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean(), any(), anyInt(), anyInt(), - anyInt(), anyInt())) + anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean(), any(), + anyInt(), anyInt(), anyInt(), anyInt())) .thenReturn(mMockHandleMenu); when(mMockMultiInstanceHelper.supportsMultiInstanceSplit(any())).thenReturn(false); when(mMockAppHeaderViewHolderFactory.create(any(), any(), any(), any(), any(), any(), any(), @@ -283,7 +285,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo)); - spyWindowDecor.relayout(taskInfo, false /* hasGlobalFocus */); + spyWindowDecor.relayout(taskInfo, false /* hasGlobalFocus */, mExclusionRegion); // Menus should close if open before the task being invisible causes relayout to return. verify(spyWindowDecor).closeHandleMenu(); @@ -303,7 +305,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { /* isKeyguardVisibleAndOccluded */ false, /* inFullImmersiveMode */ false, new InsetsState(), - /* hasGlobalFocus= */ true); + /* hasGlobalFocus= */ true, + mExclusionRegion); assertThat(relayoutParams.mShadowRadiusId).isNotEqualTo(Resources.ID_NULL); } @@ -324,7 +327,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { /* isKeyguardVisibleAndOccluded */ false, /* inFullImmersiveMode */ false, new InsetsState(), - /* hasGlobalFocus= */ true); + /* hasGlobalFocus= */ true, + mExclusionRegion); assertThat(relayoutParams.mCornerRadius).isGreaterThan(0); } @@ -350,7 +354,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { /* isKeyguardVisibleAndOccluded */ false, /* inFullImmersiveMode */ false, new InsetsState(), - /* hasGlobalFocus= */ true); + /* hasGlobalFocus= */ true, + mExclusionRegion); assertThat(relayoutParams.mWindowDecorConfig.densityDpi).isEqualTo(customTaskDensity); } @@ -377,12 +382,14 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { /* isKeyguardVisibleAndOccluded */ false, /* inFullImmersiveMode */ false, new InsetsState(), - /* hasGlobalFocus= */ true); + /* hasGlobalFocus= */ true, + mExclusionRegion); assertThat(relayoutParams.mWindowDecorConfig.densityDpi).isEqualTo(systemDensity); } @Test + @DisableFlags(Flags.FLAG_ENABLE_ACCESSIBLE_CUSTOM_HEADERS) public void updateRelayoutParams_freeformAndTransparentAppearance_allowsInputFallthrough() { final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true); taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM); @@ -400,12 +407,39 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { /* isKeyguardVisibleAndOccluded */ false, /* inFullImmersiveMode */ false, new InsetsState(), - /* hasGlobalFocus= */ true); + /* hasGlobalFocus= */ true, + mExclusionRegion); assertThat(relayoutParams.hasInputFeatureSpy()).isTrue(); } @Test + @EnableFlags(Flags.FLAG_ENABLE_ACCESSIBLE_CUSTOM_HEADERS) + public void updateRelayoutParams_freeformAndTransparentAppearance_limitedTouchRegion() { + final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true); + taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM); + taskInfo.taskDescription.setTopOpaqueSystemBarsAppearance( + APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND); + final 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.mLimitTouchRegionToSystemAreas).isTrue(); + } + + @Test + @DisableFlags(Flags.FLAG_ENABLE_ACCESSIBLE_CUSTOM_HEADERS) public void updateRelayoutParams_freeformButOpaqueAppearance_disallowsInputFallthrough() { final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true); taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM); @@ -422,12 +456,38 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { /* isKeyguardVisibleAndOccluded */ false, /* inFullImmersiveMode */ false, new InsetsState(), - /* hasGlobalFocus= */ true); + /* hasGlobalFocus= */ true, + mExclusionRegion); assertThat(relayoutParams.hasInputFeatureSpy()).isFalse(); } @Test + @EnableFlags(Flags.FLAG_ENABLE_ACCESSIBLE_CUSTOM_HEADERS) + public void updateRelayoutParams_freeformButOpaqueAppearance_unlimitedTouchRegion() { + final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true); + taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM); + taskInfo.taskDescription.setTopOpaqueSystemBarsAppearance(0); + final 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.mLimitTouchRegionToSystemAreas).isFalse(); + } + + @Test + @DisableFlags(Flags.FLAG_ENABLE_ACCESSIBLE_CUSTOM_HEADERS) public void updateRelayoutParams_fullscreen_disallowsInputFallthrough() { final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true); taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN); @@ -443,12 +503,36 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { /* isKeyguardVisibleAndOccluded */ false, /* inFullImmersiveMode */ false, new InsetsState(), - /* hasGlobalFocus= */ true); + /* hasGlobalFocus= */ true, + mExclusionRegion); assertThat(relayoutParams.hasInputFeatureSpy()).isFalse(); } @Test + @EnableFlags(Flags.FLAG_ENABLE_ACCESSIBLE_CUSTOM_HEADERS) + public void updateRelayoutParams_fullscreen_unlimitedTouchRegion() { + final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true); + taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN); + final 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.mLimitTouchRegionToSystemAreas).isFalse(); + } + + @Test public void updateRelayoutParams_freeform_inputChannelNeeded() { final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true); taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM); @@ -464,7 +548,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { /* isKeyguardVisibleAndOccluded */ false, /* inFullImmersiveMode */ false, new InsetsState(), - /* hasGlobalFocus= */ true); + /* hasGlobalFocus= */ true, + mExclusionRegion); assertThat(hasNoInputChannelFeature(relayoutParams)).isFalse(); } @@ -486,7 +571,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { /* isKeyguardVisibleAndOccluded */ false, /* inFullImmersiveMode */ false, new InsetsState(), - /* hasGlobalFocus= */ true); + /* hasGlobalFocus= */ true, + mExclusionRegion); assertThat(hasNoInputChannelFeature(relayoutParams)).isTrue(); } @@ -508,7 +594,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { /* isKeyguardVisibleAndOccluded */ false, /* inFullImmersiveMode */ false, new InsetsState(), - /* hasGlobalFocus= */ true); + /* hasGlobalFocus= */ true, + mExclusionRegion); assertThat(hasNoInputChannelFeature(relayoutParams)).isTrue(); } @@ -531,7 +618,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { /* isKeyguardVisibleAndOccluded */ false, /* inFullImmersiveMode */ false, new InsetsState(), - /* hasGlobalFocus= */ true); + /* hasGlobalFocus= */ true, + mExclusionRegion); assertThat((relayoutParams.mInsetSourceFlags & FLAG_FORCE_CONSUMING) != 0).isTrue(); } @@ -555,7 +643,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { /* isKeyguardVisibleAndOccluded */ false, /* inFullImmersiveMode */ false, new InsetsState(), - /* hasGlobalFocus= */ true); + /* hasGlobalFocus= */ true, + mExclusionRegion); assertThat((relayoutParams.mInsetSourceFlags & FLAG_FORCE_CONSUMING) == 0).isTrue(); } @@ -577,7 +666,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { /* isKeyguardVisibleAndOccluded */ false, /* inFullImmersiveMode */ false, new InsetsState(), - /* hasGlobalFocus= */ true); + /* hasGlobalFocus= */ true, + mExclusionRegion); assertThat( (relayoutParams.mInsetSourceFlags & FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR) != 0) @@ -601,7 +691,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { /* isKeyguardVisibleAndOccluded */ false, /* inFullImmersiveMode */ false, new InsetsState(), - /* hasGlobalFocus= */ true); + /* hasGlobalFocus= */ true, + mExclusionRegion); assertThat( (relayoutParams.mInsetSourceFlags & FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR) == 0) @@ -631,7 +722,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { /* isKeyguardVisibleAndOccluded */ false, /* inFullImmersiveMode */ true, insetsState, - /* hasGlobalFocus= */ true); + /* hasGlobalFocus= */ true, + mExclusionRegion); // Takes status bar inset as padding, ignores caption bar inset. assertThat(relayoutParams.mCaptionTopPadding).isEqualTo(50); @@ -654,7 +746,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { /* isKeyguardVisibleAndOccluded */ false, /* inFullImmersiveMode */ true, new InsetsState(), - /* hasGlobalFocus= */ true); + /* hasGlobalFocus= */ true, + mExclusionRegion); assertThat(relayoutParams.mIsInsetSource).isFalse(); } @@ -676,7 +769,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { /* isKeyguardVisibleAndOccluded */ false, /* inFullImmersiveMode */ false, new InsetsState(), - /* hasGlobalFocus= */ true); + /* hasGlobalFocus= */ true, + mExclusionRegion); // Header is always shown because it's assumed the status bar is always visible. assertThat(relayoutParams.mIsCaptionVisible).isTrue(); @@ -698,7 +792,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { /* isKeyguardVisibleAndOccluded */ false, /* inFullImmersiveMode */ false, new InsetsState(), - /* hasGlobalFocus= */ true); + /* hasGlobalFocus= */ true, + mExclusionRegion); assertThat(relayoutParams.mIsCaptionVisible).isTrue(); } @@ -719,7 +814,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { /* isKeyguardVisibleAndOccluded */ false, /* inFullImmersiveMode */ false, new InsetsState(), - /* hasGlobalFocus= */ true); + /* hasGlobalFocus= */ true, + mExclusionRegion); assertThat(relayoutParams.mIsCaptionVisible).isFalse(); } @@ -740,7 +836,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { /* isKeyguardVisibleAndOccluded */ true, /* inFullImmersiveMode */ false, new InsetsState(), - /* hasGlobalFocus= */ true); + /* hasGlobalFocus= */ true, + mExclusionRegion); assertThat(relayoutParams.mIsCaptionVisible).isFalse(); } @@ -762,7 +859,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { /* isKeyguardVisibleAndOccluded */ false, /* inFullImmersiveMode */ true, new InsetsState(), - /* hasGlobalFocus= */ true); + /* hasGlobalFocus= */ true, + mExclusionRegion); assertThat(relayoutParams.mIsCaptionVisible).isTrue(); @@ -776,7 +874,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { /* isKeyguardVisibleAndOccluded */ false, /* inFullImmersiveMode */ true, new InsetsState(), - /* hasGlobalFocus= */ true); + /* hasGlobalFocus= */ true, + mExclusionRegion); assertThat(relayoutParams.mIsCaptionVisible).isFalse(); } @@ -798,7 +897,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { /* isKeyguardVisibleAndOccluded */ true, /* inFullImmersiveMode */ true, new InsetsState(), - /* hasGlobalFocus= */ true); + /* hasGlobalFocus= */ true, + mExclusionRegion); assertThat(relayoutParams.mIsCaptionVisible).isFalse(); } @@ -809,7 +909,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo)); taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN); - spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */); + spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */, mExclusionRegion); verify(mMockTransaction).apply(); verify(mMockRootSurfaceControl, never()).applyTransactionOnDraw(any()); @@ -824,7 +924,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { // Make non-resizable to avoid dealing with input-permissions (MONITOR_INPUT) taskInfo.isResizeable = false; - spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */); + spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */, mExclusionRegion); verify(mMockTransaction, never()).apply(); verify(mMockRootSurfaceControl).applyTransactionOnDraw(mMockTransaction); @@ -836,7 +936,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo)); taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN); - spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */); + spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */, mExclusionRegion); verify(mMockSurfaceControlViewHostFactory, never()).create(any(), any(), any()); } @@ -848,7 +948,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN); ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class); - spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */); + spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */, mExclusionRegion); // Once for view host, the other for the AppHandle input layer. verify(mMockHandler, times(2)).post(runnableArgument.capture()); @@ -865,7 +965,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { // Make non-resizable to avoid dealing with input-permissions (MONITOR_INPUT) taskInfo.isResizeable = false; - spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */); + spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */, mExclusionRegion); verify(mMockSurfaceControlViewHostFactory).create(any(), any(), any()); verify(mMockHandler, never()).post(any()); @@ -877,11 +977,11 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo)); taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN); ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class); - spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */); + spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */, mExclusionRegion); // Once for view host, the other for the AppHandle input layer. verify(mMockHandler, times(2)).post(runnableArgument.capture()); - spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */); + spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */, mExclusionRegion); verify(mMockHandler).removeCallbacks(runnableArgument.getValue()); } @@ -892,7 +992,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo)); taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN); ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class); - spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */); + spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */, mExclusionRegion); // Once for view host, the other for the AppHandle input layer. verify(mMockHandler, times(2)).post(runnableArgument.capture()); @@ -1132,7 +1232,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { runnableArgument.getValue().run(); // Relayout decor with same captured link - decor.relayout(taskInfo, true /* hasGlobalFocus */); + decor.relayout(taskInfo, true /* hasGlobalFocus */, mExclusionRegion); // Verify handle menu's browser link not set to captured link since link is expired createHandleMenu(decor); @@ -1313,7 +1413,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo)); taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN); - spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */); + spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */, mExclusionRegion); verify(mMockCaptionHandleRepository, never()).notifyCaptionChanged(any()); } @@ -1330,7 +1430,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { ArgumentCaptor<CaptionState> captionStateArgumentCaptor = ArgumentCaptor.forClass( CaptionState.class); - spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */); + spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */, mExclusionRegion); verify(mMockCaptionHandleRepository, atLeastOnce()).notifyCaptionChanged( captionStateArgumentCaptor.capture()); @@ -1357,7 +1457,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { ArgumentCaptor<CaptionState> captionStateArgumentCaptor = ArgumentCaptor.forClass( CaptionState.class); - spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */); + spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */, mExclusionRegion); verify(mMockAppHeaderViewHolder, atLeastOnce()).runOnAppChipGlobalLayout( runnableArgumentCaptor.capture()); runnableArgumentCaptor.getValue().invoke(); @@ -1380,7 +1480,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { ArgumentCaptor<CaptionState> captionStateArgumentCaptor = ArgumentCaptor.forClass( CaptionState.class); - spyWindowDecor.relayout(taskInfo, false /* hasGlobalFocus */); + spyWindowDecor.relayout(taskInfo, false /* hasGlobalFocus */, mExclusionRegion); verify(mMockCaptionHandleRepository, atLeastOnce()).notifyCaptionChanged( captionStateArgumentCaptor.capture()); @@ -1400,7 +1500,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { ArgumentCaptor<CaptionState> captionStateArgumentCaptor = ArgumentCaptor.forClass( CaptionState.class); - spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */); + spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */, mExclusionRegion); createHandleMenu(spyWindowDecor); verify(mMockCaptionHandleRepository, atLeastOnce()).notifyCaptionChanged( @@ -1425,7 +1525,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { ArgumentCaptor<CaptionState> captionStateArgumentCaptor = ArgumentCaptor.forClass( CaptionState.class); - spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */); + spyWindowDecor.relayout(taskInfo, true /* hasGlobalFocus */, mExclusionRegion); createHandleMenu(spyWindowDecor); spyWindowDecor.closeHandleMenu(); @@ -1440,9 +1540,30 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { } + @Test + @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_TO_WEB) + public void browserApp_webUriUsedForBrowserApp() { + // Make {@link AppToWebUtils#isBrowserApp} return true + ResolveInfo resolveInfo = new ResolveInfo(); + resolveInfo.handleAllWebDataURI = true; + resolveInfo.activityInfo = createActivityInfo(); + when(mMockPackageManager.queryIntentActivitiesAsUser(any(), anyInt(), anyInt())) + .thenReturn(List.of(resolveInfo)); + + final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */); + final DesktopModeWindowDecoration decor = createWindowDecoration( + taskInfo, TEST_URI1 /* captured link */, TEST_URI2 /* web uri */, + TEST_URI3 /* generic link */); + + // Verify web uri used for browser applications + createHandleMenu(decor); + verifyHandleMenuCreated(TEST_URI2); + } + + private void verifyHandleMenuCreated(@Nullable Uri uri) { verify(mMockHandleMenuFactory).create(any(), any(), anyInt(), any(), any(), - any(), anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean(), + any(), anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean(), argThat(intent -> (uri == null && intent == null) || intent.getData().equals(uri)), anyInt(), anyInt(), anyInt(), anyInt()); } @@ -1522,7 +1643,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { windowDecor.setOpenInBrowserClickListener(mMockOpenInBrowserClickListener); windowDecor.mDecorWindowContext = mContext; if (relayout) { - windowDecor.relayout(taskInfo, true /* hasGlobalFocus */); + windowDecor.relayout(taskInfo, true /* hasGlobalFocus */, mExclusionRegion); } return windowDecor; } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt index ade17c61eda1..7ec2cbf9460e 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt @@ -242,7 +242,7 @@ class HandleMenuTest : ShellTestCase() { private fun createAndShowHandleMenu( splitPosition: Int? = null, - forceShowSystemBars: Boolean = false, + forceShowSystemBars: Boolean = false ): HandleMenu { val layoutId = if (mockDesktopWindowDecoration.mTaskInfo.isFreeform) { R.layout.desktop_mode_app_header @@ -266,8 +266,9 @@ class HandleMenuTest : ShellTestCase() { WindowManagerWrapper(mockWindowManager), layoutId, appIcon, appName, splitScreenController, shouldShowWindowingPill = true, shouldShowNewWindowButton = true, shouldShowManageWindowsButton = false, - shouldShowChangeAspectRatioButton = false, - null /* openInBrowserLink */, captionWidth = HANDLE_WIDTH, captionHeight = 50, + shouldShowChangeAspectRatioButton = false, isBrowserApp = false, + null /* openInAppOrBrowserIntent */, captionWidth = HANDLE_WIDTH, + captionHeight = 50, captionX = captionX, captionY = 0, ) @@ -278,7 +279,7 @@ class HandleMenuTest : ShellTestCase() { onNewWindowClickListener = mock(), onManageWindowsClickListener = mock(), onChangeAspectRatioClickListener = mock(), - openInBrowserClickListener = mock(), + openInAppOrBrowserClickListener = mock(), onOpenByDefaultClickListener = mock(), onCloseMenuClickListener = mock(), onOutsideTouchListener = mock(), 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 8e0434cb28f7..534803db5fe0 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 @@ -60,6 +60,7 @@ import android.content.res.Resources; import android.graphics.Color; import android.graphics.Point; import android.graphics.Rect; +import android.graphics.Region; import android.platform.test.flag.junit.SetFlagsRule; import android.testing.AndroidTestingRunner; import android.util.DisplayMetrics; @@ -508,7 +509,7 @@ public class WindowDecorationTests extends ShellTestCase { final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo); windowDecor.relayout(taskInfo, true /* applyStartTransactionOnDraw */, - true /* hasGlobalFocus */); + true /* hasGlobalFocus */, Region.obtain()); verify(mMockRootSurfaceControl).applyTransactionOnDraw(mMockSurfaceControlStartT); } @@ -525,7 +526,7 @@ public class WindowDecorationTests extends ShellTestCase { mRelayoutParams.mCaptionTopPadding = 50; windowDecor.relayout(taskInfo, false /* applyStartTransactionOnDraw */, - true /* hasGlobalFocus */); + true /* hasGlobalFocus */, Region.obtain()); assertEquals(50, mRelayoutResult.mCaptionTopPadding); } @@ -944,7 +945,7 @@ public class WindowDecorationTests extends ShellTestCase { decor.onInsetsStateChanged(createInsetsState(statusBars(), false /* visible */)); - verify(decor, times(2)).relayout(task, true /* hasGlobalFocus */); + verify(decor, times(2)).relayout(any(), any(), any(), any(), any(), any()); } @Test @@ -958,7 +959,7 @@ public class WindowDecorationTests extends ShellTestCase { decor.onInsetsStateChanged(createInsetsState(statusBars(), true /* visible */)); - verify(decor, times(1)).relayout(task, true /* hasGlobalFocus */); + verify(decor, times(1)).relayout(any(), any(), any(), any(), any(), any()); } @Test @@ -973,7 +974,7 @@ public class WindowDecorationTests extends ShellTestCase { decor.onKeyguardStateChanged(true /* visible */, true /* occluding */); assertTrue(decor.mIsKeyguardVisibleAndOccluded); - verify(decor, times(2)).relayout(task, true /* hasGlobalFocus */); + verify(decor, times(2)).relayout(any(), any(), any(), any(), any(), any()); } @Test @@ -987,7 +988,7 @@ public class WindowDecorationTests extends ShellTestCase { decor.onKeyguardStateChanged(false /* visible */, true /* occluding */); - verify(decor, times(1)).relayout(task, true /* hasGlobalFocus */); + verify(decor, times(1)).relayout(any(), any(), any(), any(), any(), any()); } private ActivityManager.RunningTaskInfo createTaskInfo() { @@ -1061,9 +1062,16 @@ public class WindowDecorationTests extends ShellTestCase { surfaceControlViewHostFactory, desktopModeEventLogger); } - @Override void relayout(ActivityManager.RunningTaskInfo taskInfo, boolean hasGlobalFocus) { - relayout(taskInfo, false /* applyStartTransactionOnDraw */, hasGlobalFocus); + relayout(taskInfo, false /* applyStartTransactionOnDraw */, hasGlobalFocus, + Region.obtain()); + } + + @Override + void relayout(ActivityManager.RunningTaskInfo taskInfo, boolean hasGlobalFocus, + @NonNull Region displayExclusionRegion) { + relayout(taskInfo, false /* applyStartTransactionOnDraw */, hasGlobalFocus, + displayExclusionRegion); } @Override @@ -1085,11 +1093,13 @@ public class WindowDecorationTests extends ShellTestCase { } void relayout(ActivityManager.RunningTaskInfo taskInfo, - boolean applyStartTransactionOnDraw, boolean hasGlobalFocus) { + boolean applyStartTransactionOnDraw, boolean hasGlobalFocus, + @NonNull Region displayExclusionRegion) { mRelayoutParams.mRunningTaskInfo = taskInfo; mRelayoutParams.mApplyStartTransactionOnDraw = applyStartTransactionOnDraw; mRelayoutParams.mLayoutResId = R.layout.caption_layout; mRelayoutParams.mHasGlobalFocus = hasGlobalFocus; + mRelayoutParams.mDisplayExclusionRegion.set(displayExclusionRegion); relayout(mRelayoutParams, mMockSurfaceControlStartT, mMockSurfaceControlFinishT, mMockWindowContainerTransaction, mMockView, mRelayoutResult); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModelTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModelTest.kt index d44c01592ff7..d8c1a11de452 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModelTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModelTest.kt @@ -24,8 +24,8 @@ import com.android.wm.shell.ShellTaskOrganizer import com.android.wm.shell.ShellTestCase import com.android.wm.shell.common.DisplayController import com.android.wm.shell.common.SyncTransactionQueue -import com.android.wm.shell.desktopmode.DesktopRepository import com.android.wm.shell.desktopmode.DesktopModeEventLogger +import com.android.wm.shell.desktopmode.DesktopRepository import com.android.wm.shell.desktopmode.DesktopTasksController import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFreeformTask import com.android.wm.shell.desktopmode.ReturnToDragStartAnimator @@ -130,7 +130,8 @@ class DesktopTilingDecorViewModelTest : ShellTestCase() { ) desktopTilingDecorViewModel.moveTaskToFrontIfTiled(task1) - verify(desktopTilingDecoration, times(1)).moveTiledPairToFront(any()) + verify(desktopTilingDecoration, times(1)) + .moveTiledPairToFront(any(), isTaskFocused = eq(true)) } @Test diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecorationTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecorationTest.kt index 057d8fa3adb0..d7b971de94ac 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecorationTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecorationTest.kt @@ -328,6 +328,37 @@ class DesktopTilingWindowDecorationTest : ShellTestCase() { } @Test + fun taskTiled_broughtToFront_taskInfoNotUpdated_bringToFront() { + val task1 = createFreeformTask() + val task2 = createFreeformTask() + val task3 = createFreeformTask() + val stableBounds = STABLE_BOUNDS_MOCK + whenever(displayLayout.getStableBounds(any())).thenAnswer { i -> + (i.arguments.first() as Rect).set(stableBounds) + } + whenever(context.resources).thenReturn(resources) + whenever(resources.getDimensionPixelSize(any())).thenReturn(split_divider_width) + whenever(desktopWindowDecoration.getLeash()).thenReturn(surfaceControlMock) + whenever(desktopRepository.isVisibleTask(any())).thenReturn(true) + tilingDecoration.onAppTiled( + task1, + desktopWindowDecoration, + DesktopTasksController.SnapPosition.RIGHT, + BOUNDS, + ) + tilingDecoration.onAppTiled( + task2, + desktopWindowDecoration, + DesktopTasksController.SnapPosition.LEFT, + BOUNDS, + ) + + assertThat(tilingDecoration.moveTiledPairToFront(task3, isTaskFocused = true)).isFalse() + assertThat(tilingDecoration.moveTiledPairToFront(task1, isTaskFocused = true)).isTrue() + verify(transitions, times(1)).startTransition(eq(TRANSIT_TO_FRONT), any(), eq(null)) + } + + @Test fun taskTiledTasks_NotResized_BeforeTouchEndArrival() { // Setup val task1 = createFreeformTask() diff --git a/libs/appfunctions/Android.bp b/libs/appfunctions/Android.bp index c6cee07d1946..5ab5a7a59c2a 100644 --- a/libs/appfunctions/Android.bp +++ b/libs/appfunctions/Android.bp @@ -18,10 +18,10 @@ package { } java_sdk_library { - name: "com.google.android.appfunctions.sidecar", + name: "com.android.extensions.appfunctions", owner: "google", srcs: ["java/**/*.java"], - api_packages: ["com.google.android.appfunctions.sidecar"], + api_packages: ["com.android.extensions.appfunctions"], dex_preopt: { enabled: false, }, @@ -31,9 +31,9 @@ java_sdk_library { } prebuilt_etc { - name: "appfunctions.sidecar.xml", + name: "appfunctions.extension.xml", system_ext_specific: true, sub_dir: "permissions", - src: "appfunctions.sidecar.xml", + src: "appfunctions.extension.xml", filename_from_src: true, } diff --git a/libs/appfunctions/api/current.txt b/libs/appfunctions/api/current.txt index faf84a8ab5ac..de402095e195 100644 --- a/libs/appfunctions/api/current.txt +++ b/libs/appfunctions/api/current.txt @@ -1,9 +1,29 @@ // Signature format: 2.0 -package com.google.android.appfunctions.sidecar { +package com.android.extensions.appfunctions { + + public final class AppFunctionException extends java.lang.Exception { + ctor public AppFunctionException(int, @Nullable String); + ctor public AppFunctionException(int, @Nullable String, @NonNull android.os.Bundle); + method public int getErrorCategory(); + method public int getErrorCode(); + method @Nullable public String getErrorMessage(); + method @NonNull public android.os.Bundle getExtras(); + field public static final int ERROR_APP_UNKNOWN_ERROR = 3000; // 0xbb8 + field public static final int ERROR_CANCELLED = 2001; // 0x7d1 + field public static final int ERROR_CATEGORY_APP = 3; // 0x3 + field public static final int ERROR_CATEGORY_REQUEST_ERROR = 1; // 0x1 + field public static final int ERROR_CATEGORY_SYSTEM = 2; // 0x2 + field public static final int ERROR_CATEGORY_UNKNOWN = 0; // 0x0 + field public static final int ERROR_DENIED = 1000; // 0x3e8 + field public static final int ERROR_DISABLED = 1002; // 0x3ea + field public static final int ERROR_FUNCTION_NOT_FOUND = 1003; // 0x3eb + field public static final int ERROR_INVALID_ARGUMENT = 1001; // 0x3e9 + field public static final int ERROR_SYSTEM_ERROR = 2000; // 0x7d0 + } public final class AppFunctionManager { ctor public AppFunctionManager(android.content.Context); - method @RequiresPermission(anyOf={android.Manifest.permission.EXECUTE_APP_FUNCTIONS_TRUSTED, android.Manifest.permission.EXECUTE_APP_FUNCTIONS}, conditional=true) public void executeAppFunction(@NonNull com.google.android.appfunctions.sidecar.ExecuteAppFunctionRequest, @NonNull java.util.concurrent.Executor, @NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer<com.google.android.appfunctions.sidecar.ExecuteAppFunctionResponse>); + method @RequiresPermission(anyOf={android.Manifest.permission.EXECUTE_APP_FUNCTIONS_TRUSTED, android.Manifest.permission.EXECUTE_APP_FUNCTIONS}, conditional=true) public void executeAppFunction(@NonNull com.android.extensions.appfunctions.ExecuteAppFunctionRequest, @NonNull java.util.concurrent.Executor, @NonNull android.os.CancellationSignal, @NonNull android.os.OutcomeReceiver<com.android.extensions.appfunctions.ExecuteAppFunctionResponse,com.android.extensions.appfunctions.AppFunctionException>); method @RequiresPermission(anyOf={android.Manifest.permission.EXECUTE_APP_FUNCTIONS_TRUSTED, android.Manifest.permission.EXECUTE_APP_FUNCTIONS}, conditional=true) public void isAppFunctionEnabled(@NonNull String, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,java.lang.Exception>); method public void isAppFunctionEnabled(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,java.lang.Exception>); method public void setAppFunctionEnabled(@NonNull String, int, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,java.lang.Exception>); @@ -15,7 +35,7 @@ package com.google.android.appfunctions.sidecar { public abstract class AppFunctionService extends android.app.Service { ctor public AppFunctionService(); method @NonNull public final android.os.IBinder onBind(@Nullable android.content.Intent); - method @MainThread public abstract void onExecuteFunction(@NonNull com.google.android.appfunctions.sidecar.ExecuteAppFunctionRequest, @NonNull String, @NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer<com.google.android.appfunctions.sidecar.ExecuteAppFunctionResponse>); + method @MainThread public abstract void onExecuteFunction(@NonNull com.android.extensions.appfunctions.ExecuteAppFunctionRequest, @NonNull String, @NonNull android.os.CancellationSignal, @NonNull android.os.OutcomeReceiver<com.android.extensions.appfunctions.ExecuteAppFunctionResponse,com.android.extensions.appfunctions.AppFunctionException>); field @NonNull public static final String BIND_APP_FUNCTION_SERVICE = "android.permission.BIND_APP_FUNCTION_SERVICE"; field @NonNull public static final String SERVICE_INTERFACE = "android.app.appfunctions.AppFunctionService"; } @@ -29,33 +49,17 @@ package com.google.android.appfunctions.sidecar { public static final class ExecuteAppFunctionRequest.Builder { ctor public ExecuteAppFunctionRequest.Builder(@NonNull String, @NonNull String); - method @NonNull public com.google.android.appfunctions.sidecar.ExecuteAppFunctionRequest build(); - method @NonNull public com.google.android.appfunctions.sidecar.ExecuteAppFunctionRequest.Builder setExtras(@NonNull android.os.Bundle); - method @NonNull public com.google.android.appfunctions.sidecar.ExecuteAppFunctionRequest.Builder setParameters(@NonNull android.app.appsearch.GenericDocument); + method @NonNull public com.android.extensions.appfunctions.ExecuteAppFunctionRequest build(); + method @NonNull public com.android.extensions.appfunctions.ExecuteAppFunctionRequest.Builder setExtras(@NonNull android.os.Bundle); + method @NonNull public com.android.extensions.appfunctions.ExecuteAppFunctionRequest.Builder setParameters(@NonNull android.app.appsearch.GenericDocument); } public final class ExecuteAppFunctionResponse { - method public int getErrorCategory(); - method @Nullable public String getErrorMessage(); + ctor public ExecuteAppFunctionResponse(@NonNull android.app.appsearch.GenericDocument); + ctor public ExecuteAppFunctionResponse(@NonNull android.app.appsearch.GenericDocument, @NonNull android.os.Bundle); method @NonNull public android.os.Bundle getExtras(); - method public int getResultCode(); method @NonNull public android.app.appsearch.GenericDocument getResultDocument(); - method public boolean isSuccess(); - method @NonNull public static com.google.android.appfunctions.sidecar.ExecuteAppFunctionResponse newFailure(int, @Nullable String, @Nullable android.os.Bundle); - method @NonNull public static com.google.android.appfunctions.sidecar.ExecuteAppFunctionResponse newSuccess(@NonNull android.app.appsearch.GenericDocument, @Nullable android.os.Bundle); - field public static final int ERROR_CATEGORY_APP = 3; // 0x3 - field public static final int ERROR_CATEGORY_REQUEST_ERROR = 1; // 0x1 - field public static final int ERROR_CATEGORY_SYSTEM = 2; // 0x2 - field public static final int ERROR_CATEGORY_UNKNOWN = 0; // 0x0 - field public static final String PROPERTY_RETURN_VALUE = "returnValue"; - field public static final int RESULT_APP_UNKNOWN_ERROR = 3000; // 0xbb8 - field public static final int RESULT_CANCELLED = 2001; // 0x7d1 - field public static final int RESULT_DENIED = 1000; // 0x3e8 - field public static final int RESULT_DISABLED = 1002; // 0x3ea - field public static final int RESULT_FUNCTION_NOT_FOUND = 1003; // 0x3eb - field public static final int RESULT_INVALID_ARGUMENT = 1001; // 0x3e9 - field public static final int RESULT_OK = 0; // 0x0 - field public static final int RESULT_SYSTEM_ERROR = 2000; // 0x7d0 + field public static final String PROPERTY_RETURN_VALUE = "androidAppfunctionsReturnValue"; } } diff --git a/libs/appfunctions/appfunctions.sidecar.xml b/libs/appfunctions/appfunctions.extension.xml index bef8b6ec7ce6..dd09cc39d12f 100644 --- a/libs/appfunctions/appfunctions.sidecar.xml +++ b/libs/appfunctions/appfunctions.extension.xml @@ -16,6 +16,6 @@ --> <permissions> <library - name="com.google.android.appfunctions.sidecar" - file="/system_ext/framework/com.google.android.appfunctions.sidecar.jar"/> + name="com.android.extensions.appfunctions" + file="/system_ext/framework/com.android.extensions.appfunctions.jar"/> </permissions>
\ No newline at end of file diff --git a/libs/appfunctions/java/com/android/extensions/appfunctions/AppFunctionException.java b/libs/appfunctions/java/com/android/extensions/appfunctions/AppFunctionException.java new file mode 100644 index 000000000000..2540236f2ce5 --- /dev/null +++ b/libs/appfunctions/java/com/android/extensions/appfunctions/AppFunctionException.java @@ -0,0 +1,212 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.extensions.appfunctions; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Bundle; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** Represents an app function related errors. */ +public final class AppFunctionException extends Exception { + /** + * The caller does not have the permission to execute an app function. + * + * <p>This error is in the {@link #ERROR_CATEGORY_REQUEST_ERROR} category. + */ + public static final int ERROR_DENIED = 1000; + + /** + * The caller supplied invalid arguments to the execution request. + * + * <p>This error may be considered similar to {@link IllegalArgumentException}. + * + * <p>This error is in the {@link #ERROR_CATEGORY_REQUEST_ERROR} category. + */ + public static final int ERROR_INVALID_ARGUMENT = 1001; + + /** + * The caller tried to execute a disabled app function. + * + * <p>This error is in the {@link #ERROR_CATEGORY_REQUEST_ERROR} category. + */ + public static final int ERROR_DISABLED = 1002; + + /** + * The caller tried to execute a function that does not exist. + * + * <p>This error is in the {@link #ERROR_CATEGORY_REQUEST_ERROR} category. + */ + public static final int ERROR_FUNCTION_NOT_FOUND = 1003; + + /** + * An internal unexpected error coming from the system. + * + * <p>This error is in the {@link #ERROR_CATEGORY_SYSTEM} category. + */ + public static final int ERROR_SYSTEM_ERROR = 2000; + + /** + * The operation was cancelled. Use this error code to report that a cancellation is done after + * receiving a cancellation signal. + * + * <p>This error is in the {@link #ERROR_CATEGORY_SYSTEM} category. + */ + public static final int ERROR_CANCELLED = 2001; + + /** + * An unknown error occurred while processing the call in the AppFunctionService. + * + * <p>This error is thrown when the service is connected in the remote application but an + * unexpected error is thrown from the bound application. + * + * <p>This error is in the {@link #ERROR_CATEGORY_APP} category. + */ + public static final int ERROR_APP_UNKNOWN_ERROR = 3000; + + /** + * The error category is unknown. + * + * <p>This is the default value for {@link #getErrorCategory}. + */ + public static final int ERROR_CATEGORY_UNKNOWN = 0; + + /** + * The error is caused by the app requesting a function execution. + * + * <p>For example, the caller provided invalid parameters in the execution request e.g. an + * invalid function ID. + * + * <p>Errors in the category fall in the range 1000-1999 inclusive. + */ + public static final int ERROR_CATEGORY_REQUEST_ERROR = 1; + + /** + * The error is caused by an issue in the system. + * + * <p>For example, the AppFunctionService implementation is not found by the system. + * + * <p>Errors in the category fall in the range 2000-2999 inclusive. + */ + public static final int ERROR_CATEGORY_SYSTEM = 2; + + /** + * The error is caused by the app providing the function. + * + * <p>For example, the app crashed when the system is executing the request. + * + * <p>Errors in the category fall in the range 3000-3999 inclusive. + */ + public static final int ERROR_CATEGORY_APP = 3; + + private final int mErrorCode; + @Nullable private final String mErrorMessage; + @NonNull private final Bundle mExtras; + + public AppFunctionException(int errorCode, @Nullable String errorMessage) { + this(errorCode, errorMessage, Bundle.EMPTY); + } + + public AppFunctionException( + int errorCode, @Nullable String errorMessage, @NonNull Bundle extras) { + super(errorMessage); + mErrorCode = errorCode; + mErrorMessage = errorMessage; + mExtras = extras; + } + + /** Returns one of the {@code ERROR} constants. */ + @ErrorCode + public int getErrorCode() { + return mErrorCode; + } + + /** Returns the error message. */ + @Nullable + public String getErrorMessage() { + return mErrorMessage; + } + + /** + * Returns the error category. + * + * <p>This method categorizes errors based on their underlying cause, allowing developers to + * implement targeted error handling and provide more informative error messages to users. It + * maps ranges of error codes to specific error categories. + * + * <p>This method returns {@code ERROR_CATEGORY_UNKNOWN} if the error code does not belong to + * any error category. + * + * <p>See {@link ErrorCategory} for a complete list of error categories and their corresponding + * error code ranges. + */ + @ErrorCategory + public int getErrorCategory() { + if (mErrorCode >= 1000 && mErrorCode < 2000) { + return ERROR_CATEGORY_REQUEST_ERROR; + } + if (mErrorCode >= 2000 && mErrorCode < 3000) { + return ERROR_CATEGORY_SYSTEM; + } + if (mErrorCode >= 3000 && mErrorCode < 4000) { + return ERROR_CATEGORY_APP; + } + return ERROR_CATEGORY_UNKNOWN; + } + + @NonNull + public Bundle getExtras() { + return mExtras; + } + + /** + * Error codes. + * + * @hide + */ + @IntDef( + prefix = {"ERROR_"}, + value = { + ERROR_DENIED, + ERROR_APP_UNKNOWN_ERROR, + ERROR_FUNCTION_NOT_FOUND, + ERROR_SYSTEM_ERROR, + ERROR_INVALID_ARGUMENT, + ERROR_DISABLED, + ERROR_CANCELLED + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ErrorCode {} + + /** + * Error categories. + * + * @hide + */ + @IntDef( + prefix = {"ERROR_CATEGORY_"}, + value = { + ERROR_CATEGORY_UNKNOWN, + ERROR_CATEGORY_REQUEST_ERROR, + ERROR_CATEGORY_APP, + ERROR_CATEGORY_SYSTEM + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ErrorCategory {} +} diff --git a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionManager.java b/libs/appfunctions/java/com/android/extensions/appfunctions/AppFunctionManager.java index 2075104ff868..9eb66a33fedc 100644 --- a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionManager.java +++ b/libs/appfunctions/java/com/android/extensions/appfunctions/AppFunctionManager.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.android.appfunctions.sidecar; +package com.android.extensions.appfunctions; import android.Manifest; import android.annotation.CallbackExecutor; @@ -31,7 +31,6 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Objects; import java.util.concurrent.Executor; -import java.util.function.Consumer; /** * Provides app functions related functionalities. @@ -115,7 +114,9 @@ public final class AppFunctionManager { @NonNull ExecuteAppFunctionRequest sidecarRequest, @NonNull @CallbackExecutor Executor executor, @NonNull CancellationSignal cancellationSignal, - @NonNull Consumer<ExecuteAppFunctionResponse> callback) { + @NonNull + OutcomeReceiver<ExecuteAppFunctionResponse, AppFunctionException> + callback) { Objects.requireNonNull(sidecarRequest); Objects.requireNonNull(executor); Objects.requireNonNull(callback); @@ -126,10 +127,20 @@ public final class AppFunctionManager { platformRequest, executor, cancellationSignal, - (platformResponse) -> { - callback.accept( - SidecarConverter.getSidecarExecuteAppFunctionResponse( - platformResponse)); + new OutcomeReceiver<>() { + @Override + public void onResult( + android.app.appfunctions.ExecuteAppFunctionResponse result) { + callback.onResult( + SidecarConverter.getSidecarExecuteAppFunctionResponse(result)); + } + + @Override + public void onError( + android.app.appfunctions.AppFunctionException exception) { + callback.onError( + SidecarConverter.getSidecarAppFunctionException(exception)); + } }); } diff --git a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionService.java b/libs/appfunctions/java/com/android/extensions/appfunctions/AppFunctionService.java index 0dc87e45b7e3..55f579138218 100644 --- a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/AppFunctionService.java +++ b/libs/appfunctions/java/com/android/extensions/appfunctions/AppFunctionService.java @@ -14,9 +14,10 @@ * limitations under the License. */ -package com.google.android.appfunctions.sidecar; +package com.android.extensions.appfunctions; -import static android.Manifest.permission.BIND_APP_FUNCTION_SERVICE; +import static com.android.extensions.appfunctions.SidecarConverter.getPlatformAppFunctionException; +import static com.android.extensions.appfunctions.SidecarConverter.getPlatformExecuteAppFunctionResponse; import android.annotation.MainThread; import android.annotation.NonNull; @@ -26,9 +27,7 @@ import android.content.Intent; import android.os.Binder; import android.os.CancellationSignal; import android.os.IBinder; -import android.util.Log; - -import java.util.function.Consumer; +import android.os.OutcomeReceiver; /** * Abstract base class to provide app functions to the system. @@ -80,10 +79,18 @@ public abstract class AppFunctionService extends Service { platformRequest), callingPackage, cancellationSignal, - (sidecarResponse) -> { - callback.accept( - SidecarConverter.getPlatformExecuteAppFunctionResponse( - sidecarResponse)); + new OutcomeReceiver<>() { + @Override + public void onResult(ExecuteAppFunctionResponse result) { + callback.onResult( + getPlatformExecuteAppFunctionResponse(result)); + } + + @Override + public void onError(AppFunctionException exception) { + callback.onError( + getPlatformAppFunctionException(exception)); + } }); }); @@ -116,12 +123,14 @@ public abstract class AppFunctionService extends Service { * @param request The function execution request. * @param callingPackage The package name of the app that is requesting the execution. * @param cancellationSignal A signal to cancel the execution. - * @param callback A callback to report back the result. + * @param callback A callback to report back the result or error. */ @MainThread public abstract void onExecuteFunction( @NonNull ExecuteAppFunctionRequest request, @NonNull String callingPackage, @NonNull CancellationSignal cancellationSignal, - @NonNull Consumer<ExecuteAppFunctionResponse> callback); + @NonNull + OutcomeReceiver<ExecuteAppFunctionResponse, AppFunctionException> + callback); } diff --git a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/ExecuteAppFunctionRequest.java b/libs/appfunctions/java/com/android/extensions/appfunctions/ExecuteAppFunctionRequest.java index 593c5213dd52..baddc245f0f1 100644 --- a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/ExecuteAppFunctionRequest.java +++ b/libs/appfunctions/java/com/android/extensions/appfunctions/ExecuteAppFunctionRequest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.android.appfunctions.sidecar; +package com.android.extensions.appfunctions; import android.annotation.NonNull; import android.app.appsearch.GenericDocument; @@ -91,8 +91,8 @@ public final class ExecuteAppFunctionRequest { * Returns the function parameters. The key is the parameter name, and the value is the * parameter value. * - * <p>The bundle may have missing parameters. Developers are advised to implement defensive - * handling measures. + * <p>The {@link GenericDocument} may have missing parameters. Developers are advised to + * implement defensive handling measures. * * <p>Similar to {@link #getFunctionIdentifier()} the parameters required by a function can be * obtained by querying AppSearch for the corresponding {@code AppFunctionStaticMetadata}. This diff --git a/libs/appfunctions/java/com/android/extensions/appfunctions/ExecuteAppFunctionResponse.java b/libs/appfunctions/java/com/android/extensions/appfunctions/ExecuteAppFunctionResponse.java new file mode 100644 index 000000000000..0826f04a50dd --- /dev/null +++ b/libs/appfunctions/java/com/android/extensions/appfunctions/ExecuteAppFunctionResponse.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.extensions.appfunctions; + +import android.annotation.NonNull; +import android.app.appfunctions.AppFunctionManager; +import android.app.appsearch.GenericDocument; +import android.os.Bundle; + +import java.util.Objects; + +/** The response to an app function execution. */ +public final class ExecuteAppFunctionResponse { + /** + * The name of the property that stores the function return value within the {@code + * resultDocument}. + * + * <p>See {@link GenericDocument#getProperty(String)} for more information. + * + * <p>If the function returns {@code void} or throws an error, the {@code resultDocument} will + * be empty {@link GenericDocument}. + * + * <p>If the {@code resultDocument} is empty, {@link GenericDocument#getProperty(String)} will + * return {@code null}. + * + * <p>See {@link #getResultDocument} for more information on extracting the return value. + */ + public static final String PROPERTY_RETURN_VALUE = "androidAppfunctionsReturnValue"; + + /** + * Returns the return value of the executed function. + * + * <p>The return value is stored in a {@link GenericDocument} with the key {@link + * #PROPERTY_RETURN_VALUE}. + * + * <p>See {@link #getResultDocument} for more information on extracting the return value. + */ + @NonNull private final GenericDocument mResultDocument; + + /** Returns the additional metadata data relevant to this function execution response. */ + @NonNull private final Bundle mExtras; + + /** + * @param resultDocument The return value of the executed function. + */ + public ExecuteAppFunctionResponse(@NonNull GenericDocument resultDocument) { + this(resultDocument, Bundle.EMPTY); + } + + /** + * @param resultDocument The return value of the executed function. + * @param extras The additional metadata for this function execution response. + */ + public ExecuteAppFunctionResponse( + @NonNull GenericDocument resultDocument, @NonNull Bundle extras) { + mResultDocument = Objects.requireNonNull(resultDocument); + mExtras = Objects.requireNonNull(extras); + } + + /** + * Returns a generic document containing the return value of the executed function. + * + * <p>The {@link #PROPERTY_RETURN_VALUE} key can be used to obtain the return value. + * + * <p>Sample code for extracting the return value: + * + * <pre> + * GenericDocument resultDocument = response.getResultDocument(); + * Object returnValue = resultDocument.getProperty(PROPERTY_RETURN_VALUE); + * if (returnValue != null) { + * // Cast returnValue to expected type, or use {@link GenericDocument#getPropertyString}, + * // {@link GenericDocument#getPropertyLong} etc. + * // Do something with the returnValue + * } + * </pre> + * + * @see AppFunctionManager on how to determine the expected function return. + */ + @NonNull + public GenericDocument getResultDocument() { + return mResultDocument; + } + + /** Returns the additional metadata for this function execution response. */ + @NonNull + public Bundle getExtras() { + return mExtras; + } +} diff --git a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/SidecarConverter.java b/libs/appfunctions/java/com/android/extensions/appfunctions/SidecarConverter.java index b1b05f79f33f..5e1fc7e684e2 100644 --- a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/SidecarConverter.java +++ b/libs/appfunctions/java/com/android/extensions/appfunctions/SidecarConverter.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.android.appfunctions.sidecar; +package com.android.extensions.appfunctions; import android.annotation.NonNull; @@ -28,46 +28,50 @@ public final class SidecarConverter { private SidecarConverter() {} /** - * Converts sidecar's {@link com.google.android.appfunctions.sidecar.ExecuteAppFunctionRequest} - * into platform's {@link android.app.appfunctions.ExecuteAppFunctionRequest} + * Converts sidecar's {@link ExecuteAppFunctionRequest} into platform's {@link + * android.app.appfunctions.ExecuteAppFunctionRequest} * * @hide */ @NonNull public static android.app.appfunctions.ExecuteAppFunctionRequest getPlatformExecuteAppFunctionRequest(@NonNull ExecuteAppFunctionRequest request) { - return new - android.app.appfunctions.ExecuteAppFunctionRequest.Builder( - request.getTargetPackageName(), - request.getFunctionIdentifier()) + return new android.app.appfunctions.ExecuteAppFunctionRequest.Builder( + request.getTargetPackageName(), request.getFunctionIdentifier()) .setExtras(request.getExtras()) .setParameters(request.getParameters()) .build(); } /** - * Converts sidecar's {@link com.google.android.appfunctions.sidecar.ExecuteAppFunctionResponse} - * into platform's {@link android.app.appfunctions.ExecuteAppFunctionResponse} + * Converts sidecar's {@link ExecuteAppFunctionResponse} into platform's {@link + * android.app.appfunctions.ExecuteAppFunctionResponse} * * @hide */ @NonNull public static android.app.appfunctions.ExecuteAppFunctionResponse getPlatformExecuteAppFunctionResponse(@NonNull ExecuteAppFunctionResponse response) { - if (response.isSuccess()) { - return android.app.appfunctions.ExecuteAppFunctionResponse.newSuccess( - response.getResultDocument(), response.getExtras()); - } else { - return android.app.appfunctions.ExecuteAppFunctionResponse.newFailure( - response.getResultCode(), - response.getErrorMessage(), - response.getExtras()); - } + return new android.app.appfunctions.ExecuteAppFunctionResponse( + response.getResultDocument(), response.getExtras()); } /** - * Converts platform's {@link android.app.appfunctions.ExecuteAppFunctionRequest} - * into sidecar's {@link com.google.android.appfunctions.sidecar.ExecuteAppFunctionRequest} + * Converts sidecar's {@link AppFunctionException} into platform's {@link + * android.app.appfunctions.AppFunctionException} + * + * @hide + */ + @NonNull + public static android.app.appfunctions.AppFunctionException + getPlatformAppFunctionException(@NonNull AppFunctionException exception) { + return new android.app.appfunctions.AppFunctionException( + exception.getErrorCode(), exception.getErrorMessage(), exception.getExtras()); + } + + /** + * Converts platform's {@link android.app.appfunctions.ExecuteAppFunctionRequest} into sidecar's + * {@link ExecuteAppFunctionRequest} * * @hide */ @@ -75,30 +79,34 @@ public final class SidecarConverter { public static ExecuteAppFunctionRequest getSidecarExecuteAppFunctionRequest( @NonNull android.app.appfunctions.ExecuteAppFunctionRequest request) { return new ExecuteAppFunctionRequest.Builder( - request.getTargetPackageName(), - request.getFunctionIdentifier()) + request.getTargetPackageName(), request.getFunctionIdentifier()) .setExtras(request.getExtras()) .setParameters(request.getParameters()) .build(); } /** - * Converts platform's {@link android.app.appfunctions.ExecuteAppFunctionResponse} - * into sidecar's {@link com.google.android.appfunctions.sidecar.ExecuteAppFunctionResponse} + * Converts platform's {@link android.app.appfunctions.ExecuteAppFunctionResponse} into + * sidecar's {@link ExecuteAppFunctionResponse} * * @hide */ @NonNull public static ExecuteAppFunctionResponse getSidecarExecuteAppFunctionResponse( @NonNull android.app.appfunctions.ExecuteAppFunctionResponse response) { - if (response.isSuccess()) { - return ExecuteAppFunctionResponse.newSuccess( - response.getResultDocument(), response.getExtras()); - } else { - return ExecuteAppFunctionResponse.newFailure( - response.getResultCode(), - response.getErrorMessage(), - response.getExtras()); - } + return new ExecuteAppFunctionResponse(response.getResultDocument(), response.getExtras()); + } + + /** + * Converts platform's {@link android.app.appfunctions.AppFunctionException} into + * sidecar's {@link AppFunctionException} + * + * @hide + */ + @NonNull + public static AppFunctionException getSidecarAppFunctionException( + @NonNull android.app.appfunctions.AppFunctionException exception) { + return new AppFunctionException( + exception.getErrorCode(), exception.getErrorMessage(), exception.getExtras()); } } diff --git a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/ExecuteAppFunctionResponse.java b/libs/appfunctions/java/com/google/android/appfunctions/sidecar/ExecuteAppFunctionResponse.java deleted file mode 100644 index 4e88fb025a9d..000000000000 --- a/libs/appfunctions/java/com/google/android/appfunctions/sidecar/ExecuteAppFunctionResponse.java +++ /dev/null @@ -1,359 +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.google.android.appfunctions.sidecar; - -import android.annotation.IntDef; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.app.appsearch.GenericDocument; -import android.os.Bundle; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.Objects; - -/** - * The response to an app function execution. - * - * <p>This class copies {@link android.app.appfunctions.ExecuteAppFunctionResponse} without parcel - * functionality and exposes it here as a sidecar library (avoiding direct dependency on the - * platform API). - */ -public final class ExecuteAppFunctionResponse { - /** - * The name of the property that stores the function return value within the {@code - * resultDocument}. - * - * <p>See {@link GenericDocument#getProperty(String)} for more information. - * - * <p>If the function returns {@code void} or throws an error, the {@code resultDocument} will - * be empty {@link GenericDocument}. - * - * <p>If the {@code resultDocument} is empty, {@link GenericDocument#getProperty(String)} will - * return {@code null}. - * - * <p>See {@link #getResultDocument} for more information on extracting the return value. - */ - public static final String PROPERTY_RETURN_VALUE = "returnValue"; - - /** - * The call was successful. - * - * <p>This result code does not belong in an error category. - */ - public static final int RESULT_OK = 0; - - /** - * The caller does not have the permission to execute an app function. - * - * <p>This error is in the {@link #ERROR_CATEGORY_REQUEST_ERROR} category. - */ - public static final int RESULT_DENIED = 1000; - - /** - * The caller supplied invalid arguments to the execution request. - * - * <p>This error may be considered similar to {@link IllegalArgumentException}. - * - * <p>This error is in the {@link #ERROR_CATEGORY_REQUEST_ERROR} category. - */ - public static final int RESULT_INVALID_ARGUMENT = 1001; - - /** - * The caller tried to execute a disabled app function. - * - * <p>This error is in the {@link #ERROR_CATEGORY_REQUEST_ERROR} category. - */ - public static final int RESULT_DISABLED = 1002; - - /** - * The caller tried to execute a function that does not exist. - * - * <p>This error is in the {@link #ERROR_CATEGORY_REQUEST_ERROR} category. - */ - public static final int RESULT_FUNCTION_NOT_FOUND = 1003; - - /** - * An internal unexpected error coming from the system. - * - * <p>This error is in the {@link #ERROR_CATEGORY_SYSTEM} category. - */ - public static final int RESULT_SYSTEM_ERROR = 2000; - - /** - * The operation was cancelled. Use this error code to report that a cancellation is done after - * receiving a cancellation signal. - * - * <p>This error is in the {@link #ERROR_CATEGORY_SYSTEM} category. - */ - public static final int RESULT_CANCELLED = 2001; - - /** - * An unknown error occurred while processing the call in the AppFunctionService. - * - * <p>This error is thrown when the service is connected in the remote application but an - * unexpected error is thrown from the bound application. - * - * <p>This error is in the {@link #ERROR_CATEGORY_APP} category. - */ - public static final int RESULT_APP_UNKNOWN_ERROR = 3000; - - /** - * The error category is unknown. - * - * <p>This is the default value for {@link #getErrorCategory}. - */ - public static final int ERROR_CATEGORY_UNKNOWN = 0; - - /** - * The error is caused by the app requesting a function execution. - * - * <p>For example, the caller provided invalid parameters in the execution request e.g. an - * invalid function ID. - * - * <p>Errors in the category fall in the range 1000-1999 inclusive. - */ - public static final int ERROR_CATEGORY_REQUEST_ERROR = 1; - - /** - * The error is caused by an issue in the system. - * - * <p>For example, the AppFunctionService implementation is not found by the system. - * - * <p>Errors in the category fall in the range 2000-2999 inclusive. - */ - public static final int ERROR_CATEGORY_SYSTEM = 2; - - /** - * The error is caused by the app providing the function. - * - * <p>For example, the app crashed when the system is executing the request. - * - * <p>Errors in the category fall in the range 3000-3999 inclusive. - */ - public static final int ERROR_CATEGORY_APP = 3; - - /** The result code of the app function execution. */ - @ResultCode private final int mResultCode; - - /** - * The error message associated with the result, if any. This is {@code null} if the result code - * is {@link #RESULT_OK}. - */ - @Nullable private final String mErrorMessage; - - /** - * Returns the return value of the executed function. - * - * <p>The return value is stored in a {@link GenericDocument} with the key {@link - * #PROPERTY_RETURN_VALUE}. - * - * <p>See {@link #getResultDocument} for more information on extracting the return value. - */ - @NonNull private final GenericDocument mResultDocument; - - /** Returns the additional metadata data relevant to this function execution response. */ - @NonNull private final Bundle mExtras; - - private ExecuteAppFunctionResponse( - @NonNull GenericDocument resultDocument, - @NonNull Bundle extras, - @ResultCode int resultCode, - @Nullable String errorMessage) { - mResultDocument = Objects.requireNonNull(resultDocument); - mExtras = Objects.requireNonNull(extras); - mResultCode = resultCode; - mErrorMessage = errorMessage; - } - - /** - * Returns result codes from throwable. - * - * @hide - */ - static @ResultCode int getResultCode(@NonNull Throwable t) { - if (t instanceof IllegalArgumentException) { - return ExecuteAppFunctionResponse.RESULT_INVALID_ARGUMENT; - } - return ExecuteAppFunctionResponse.RESULT_APP_UNKNOWN_ERROR; - } - - /** - * Returns a successful response. - * - * @param resultDocument The return value of the executed function. - * @param extras The additional metadata data relevant to this function execution response. - */ - @NonNull - public static ExecuteAppFunctionResponse newSuccess( - @NonNull GenericDocument resultDocument, @Nullable Bundle extras) { - Objects.requireNonNull(resultDocument); - Bundle actualExtras = getActualExtras(extras); - - return new ExecuteAppFunctionResponse( - resultDocument, actualExtras, RESULT_OK, /* errorMessage= */ null); - } - - /** - * Returns a failure response. - * - * @param resultCode The result code of the app function execution. - * @param extras The additional metadata data relevant to this function execution response. - * @param errorMessage The error message associated with the result, if any. - */ - @NonNull - public static ExecuteAppFunctionResponse newFailure( - @ResultCode int resultCode, @Nullable String errorMessage, @Nullable Bundle extras) { - if (resultCode == RESULT_OK) { - throw new IllegalArgumentException("resultCode must not be RESULT_OK"); - } - Bundle actualExtras = getActualExtras(extras); - GenericDocument emptyDocument = new GenericDocument.Builder<>("", "", "").build(); - return new ExecuteAppFunctionResponse( - emptyDocument, actualExtras, resultCode, errorMessage); - } - - private static Bundle getActualExtras(@Nullable Bundle extras) { - if (extras == null) { - return Bundle.EMPTY; - } - return extras; - } - - /** - * Returns the error category of the {@link ExecuteAppFunctionResponse}. - * - * <p>This method categorizes errors based on their underlying cause, allowing developers to - * implement targeted error handling and provide more informative error messages to users. It - * maps ranges of result codes to specific error categories. - * - * <p>When constructing a {@link #newFailure} response, use the appropriate result code value to - * ensure correct categorization of the failed response. - * - * <p>This method returns {@code ERROR_CATEGORY_UNKNOWN} if the result code does not belong to - * any error category, for example, in the case of a successful result with {@link #RESULT_OK}. - * - * <p>See {@link ErrorCategory} for a complete list of error categories and their corresponding - * result code ranges. - */ - @ErrorCategory - public int getErrorCategory() { - if (mResultCode >= 1000 && mResultCode < 2000) { - return ERROR_CATEGORY_REQUEST_ERROR; - } - if (mResultCode >= 2000 && mResultCode < 3000) { - return ERROR_CATEGORY_SYSTEM; - } - if (mResultCode >= 3000 && mResultCode < 4000) { - return ERROR_CATEGORY_APP; - } - return ERROR_CATEGORY_UNKNOWN; - } - - /** - * Returns a generic document containing the return value of the executed function. - * - * <p>The {@link #PROPERTY_RETURN_VALUE} key can be used to obtain the return value. - * - * <p>An empty document is returned if {@link #isSuccess} is {@code false} or if the executed - * function does not produce a return value. - * - * <p>Sample code for extracting the return value: - * - * <pre> - * GenericDocument resultDocument = response.getResultDocument(); - * Object returnValue = resultDocument.getProperty(PROPERTY_RETURN_VALUE); - * if (returnValue != null) { - * // Cast returnValue to expected type, or use {@link GenericDocument#getPropertyString}, - * // {@link GenericDocument#getPropertyLong} etc. - * // Do something with the returnValue - * } - * </pre> - */ - @NonNull - public GenericDocument getResultDocument() { - return mResultDocument; - } - - /** Returns the extras of the app function execution. */ - @NonNull - public Bundle getExtras() { - return mExtras; - } - - /** - * Returns {@code true} if {@link #getResultCode} equals {@link - * ExecuteAppFunctionResponse#RESULT_OK}. - */ - public boolean isSuccess() { - return getResultCode() == RESULT_OK; - } - - /** - * Returns one of the {@code RESULT} constants defined in {@link ExecuteAppFunctionResponse}. - */ - @ResultCode - public int getResultCode() { - return mResultCode; - } - - /** - * Returns the error message associated with this result. - * - * <p>If {@link #isSuccess} is {@code true}, the error message is always {@code null}. - */ - @Nullable - public String getErrorMessage() { - return mErrorMessage; - } - - /** - * Result codes. - * - * @hide - */ - @IntDef( - prefix = {"RESULT_"}, - value = { - RESULT_OK, - RESULT_DENIED, - RESULT_APP_UNKNOWN_ERROR, - RESULT_SYSTEM_ERROR, - RESULT_FUNCTION_NOT_FOUND, - RESULT_INVALID_ARGUMENT, - RESULT_DISABLED, - RESULT_CANCELLED - }) - @Retention(RetentionPolicy.SOURCE) - public @interface ResultCode {} - - /** - * Error categories. - * - * @hide - */ - @IntDef( - prefix = {"ERROR_CATEGORY_"}, - value = { - ERROR_CATEGORY_UNKNOWN, - ERROR_CATEGORY_REQUEST_ERROR, - ERROR_CATEGORY_APP, - ERROR_CATEGORY_SYSTEM - }) - @Retention(RetentionPolicy.SOURCE) - public @interface ErrorCategory {} -} diff --git a/libs/appfunctions/tests/Android.bp b/libs/appfunctions/tests/Android.bp index 6f5eff305d8d..db79675ae9f7 100644 --- a/libs/appfunctions/tests/Android.bp +++ b/libs/appfunctions/tests/Android.bp @@ -25,7 +25,7 @@ android_test { "androidx.test.rules", "androidx.test.ext.junit", "androidx.core_core-ktx", - "com.google.android.appfunctions.sidecar.impl", + "com.android.extensions.appfunctions.impl", "junit", "kotlin-test", "mockito-target-extended-minus-junit4", diff --git a/libs/appfunctions/tests/src/com/google/android/appfunctions/sidecar/tests/SidecarConverterTest.kt b/libs/appfunctions/tests/src/com/android/extensions/appfunctions/tests/SidecarConverterTest.kt index 264f84209caf..11202d58e484 100644 --- a/libs/appfunctions/tests/src/com/google/android/appfunctions/sidecar/tests/SidecarConverterTest.kt +++ b/libs/appfunctions/tests/src/com/android/extensions/appfunctions/tests/SidecarConverterTest.kt @@ -14,14 +14,15 @@ * limitations under the License. */ -package com.google.android.appfunctions.sidecar.tests +package com.android.extensions.appfunctions.tests +import android.app.appfunctions.AppFunctionException import android.app.appfunctions.ExecuteAppFunctionRequest import android.app.appfunctions.ExecuteAppFunctionResponse import android.app.appsearch.GenericDocument import android.os.Bundle import androidx.test.ext.junit.runners.AndroidJUnit4 -import com.google.android.appfunctions.sidecar.SidecarConverter +import com.android.extensions.appfunctions.SidecarConverter import com.google.common.truth.Truth.assertThat import org.junit.Test import org.junit.runner.RunWith @@ -60,7 +61,7 @@ class SidecarConverterTest { .setPropertyLong("testLong", 23) .build() val sidecarRequest = - com.google.android.appfunctions.sidecar.ExecuteAppFunctionRequest.Builder( + com.android.extensions.appfunctions.ExecuteAppFunctionRequest.Builder( "targetPkg", "targetFunctionId" ) @@ -83,44 +84,38 @@ class SidecarConverterTest { GenericDocument.Builder<GenericDocument.Builder<*>>("", "", "") .setPropertyBoolean(ExecuteAppFunctionResponse.PROPERTY_RETURN_VALUE, true) .build() - val platformResponse = ExecuteAppFunctionResponse.newSuccess(resultGd, null) + val platformResponse = ExecuteAppFunctionResponse(resultGd) val sidecarResponse = SidecarConverter.getSidecarExecuteAppFunctionResponse( platformResponse ) - assertThat(sidecarResponse.isSuccess).isTrue() assertThat( sidecarResponse.resultDocument.getProperty( ExecuteAppFunctionResponse.PROPERTY_RETURN_VALUE ) ) .isEqualTo(booleanArrayOf(true)) - assertThat(sidecarResponse.resultCode).isEqualTo(ExecuteAppFunctionResponse.RESULT_OK) - assertThat(sidecarResponse.errorMessage).isNull() } @Test - fun getSidecarExecuteAppFunctionResponse_errorResponse_sameContents() { - val emptyGd = GenericDocument.Builder<GenericDocument.Builder<*>>("", "", "").build() - val platformResponse = - ExecuteAppFunctionResponse.newFailure( - ExecuteAppFunctionResponse.RESULT_SYSTEM_ERROR, - null, - null + fun getSidecarAppFunctionException_sameContents() { + val bundle = Bundle() + bundle.putString("key", "value") + val platformException = + AppFunctionException( + AppFunctionException.ERROR_SYSTEM_ERROR, + "error", + bundle ) - val sidecarResponse = SidecarConverter.getSidecarExecuteAppFunctionResponse( - platformResponse + val sidecarException = SidecarConverter.getSidecarAppFunctionException( + platformException ) - assertThat(sidecarResponse.isSuccess).isFalse() - assertThat(sidecarResponse.resultDocument.namespace).isEqualTo(emptyGd.namespace) - assertThat(sidecarResponse.resultDocument.id).isEqualTo(emptyGd.id) - assertThat(sidecarResponse.resultDocument.schemaType).isEqualTo(emptyGd.schemaType) - assertThat(sidecarResponse.resultCode) - .isEqualTo(ExecuteAppFunctionResponse.RESULT_SYSTEM_ERROR) - assertThat(sidecarResponse.errorMessage).isNull() + assertThat(sidecarException.errorCode).isEqualTo(AppFunctionException.ERROR_SYSTEM_ERROR) + assertThat(sidecarException.errorMessage).isEqualTo("error") + assertThat(sidecarException.extras.getString("key")).isEqualTo("value") } @Test @@ -129,44 +124,39 @@ class SidecarConverterTest { GenericDocument.Builder<GenericDocument.Builder<*>>("", "", "") .setPropertyBoolean(ExecuteAppFunctionResponse.PROPERTY_RETURN_VALUE, true) .build() - val sidecarResponse = com.google.android.appfunctions.sidecar.ExecuteAppFunctionResponse - .newSuccess(resultGd, null) + val sidecarResponse = + com.android.extensions.appfunctions.ExecuteAppFunctionResponse(resultGd) val platformResponse = SidecarConverter.getPlatformExecuteAppFunctionResponse( sidecarResponse ) - assertThat(platformResponse.isSuccess).isTrue() assertThat( platformResponse.resultDocument.getProperty( ExecuteAppFunctionResponse.PROPERTY_RETURN_VALUE ) ) .isEqualTo(booleanArrayOf(true)) - assertThat(platformResponse.resultCode).isEqualTo(ExecuteAppFunctionResponse.RESULT_OK) - assertThat(platformResponse.errorMessage).isNull() } @Test - fun getPlatformExecuteAppFunctionResponse_errorResponse_sameContents() { - val emptyGd = GenericDocument.Builder<GenericDocument.Builder<*>>("", "", "").build() - val sidecarResponse = - com.google.android.appfunctions.sidecar.ExecuteAppFunctionResponse.newFailure( - ExecuteAppFunctionResponse.RESULT_SYSTEM_ERROR, - null, - null + fun getPlatformAppFunctionException_sameContents() { + val bundle = Bundle() + bundle.putString("key", "value") + val sidecarException = + com.android.extensions.appfunctions.AppFunctionException( + AppFunctionException.ERROR_SYSTEM_ERROR, + "error", + bundle ) - val platformResponse = SidecarConverter.getPlatformExecuteAppFunctionResponse( - sidecarResponse + val platformException = SidecarConverter.getPlatformAppFunctionException( + sidecarException ) - assertThat(platformResponse.isSuccess).isFalse() - assertThat(platformResponse.resultDocument.namespace).isEqualTo(emptyGd.namespace) - assertThat(platformResponse.resultDocument.id).isEqualTo(emptyGd.id) - assertThat(platformResponse.resultDocument.schemaType).isEqualTo(emptyGd.schemaType) - assertThat(platformResponse.resultCode) - .isEqualTo(ExecuteAppFunctionResponse.RESULT_SYSTEM_ERROR) - assertThat(platformResponse.errorMessage).isNull() + assertThat(platformException.errorCode) + .isEqualTo(AppFunctionException.ERROR_SYSTEM_ERROR) + assertThat(platformException.errorMessage).isEqualTo("error") + assertThat(platformException.extras.getString("key")).isEqualTo("value") } } diff --git a/libs/hwui/FeatureFlags.h b/libs/hwui/FeatureFlags.h index fddcf29b9197..5f84f47b725d 100644 --- a/libs/hwui/FeatureFlags.h +++ b/libs/hwui/FeatureFlags.h @@ -33,9 +33,9 @@ inline bool letter_spacing_justification() { #endif // __ANDROID__ } -inline bool typeface_redesign() { +inline bool typeface_redesign_readonly() { #ifdef __ANDROID__ - static bool flag = com_android_text_flags_typeface_redesign(); + static bool flag = com_android_text_flags_typeface_redesign_readonly(); return flag; #else return true; diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp index ae46a99f09c8..064cac2a6fc6 100644 --- a/libs/hwui/Properties.cpp +++ b/libs/hwui/Properties.cpp @@ -113,7 +113,6 @@ float Properties::maxHdrHeadroomOn8bit = 5.f; // TODO: Refine this number bool Properties::clipSurfaceViews = false; bool Properties::hdr10bitPlus = false; bool Properties::skipTelemetry = false; -bool Properties::resampleGainmapRegions = false; bool Properties::queryGlobalPriority = false; int Properties::timeoutMultiplier = 1; @@ -190,8 +189,6 @@ bool Properties::load() { clipSurfaceViews = base::GetBoolProperty("debug.hwui.clip_surfaceviews", hwui_flags::clip_surfaceviews()); hdr10bitPlus = hwui_flags::hdr_10bit_plus(); - resampleGainmapRegions = base::GetBoolProperty("debug.hwui.resample_gainmap_regions", - hwui_flags::resample_gainmap_regions()); queryGlobalPriority = hwui_flags::query_global_priority(); timeoutMultiplier = android::base::GetIntProperty("ro.hw_timeout_multiplier", 1); @@ -288,5 +285,11 @@ bool Properties::initializeGlAlways() { return base::GetBoolProperty(PROPERTY_INITIALIZE_GL_ALWAYS, hwui_flags::initialize_gl_always()); } +bool Properties::resampleGainmapRegions() { + static bool sResampleGainmapRegions = base::GetBoolProperty( + "debug.hwui.resample_gainmap_regions", hwui_flags::resample_gainmap_regions()); + return sResampleGainmapRegions; +} + } // namespace uirenderer } // namespace android diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h index 6f84796fb11e..db930f3904de 100644 --- a/libs/hwui/Properties.h +++ b/libs/hwui/Properties.h @@ -345,7 +345,6 @@ public: static bool clipSurfaceViews; static bool hdr10bitPlus; static bool skipTelemetry; - static bool resampleGainmapRegions; static bool queryGlobalPriority; static int timeoutMultiplier; @@ -381,6 +380,7 @@ public: static void setDrawingEnabled(bool enable); static bool initializeGlAlways(); + static bool resampleGainmapRegions(); private: static StretchEffectBehavior stretchEffectBehavior; diff --git a/libs/hwui/aconfig/hwui_flags.aconfig b/libs/hwui/aconfig/hwui_flags.aconfig index 5ad788c67816..fa27af671be6 100644 --- a/libs/hwui/aconfig/hwui_flags.aconfig +++ b/libs/hwui/aconfig/hwui_flags.aconfig @@ -154,3 +154,13 @@ flag { description: "API's that enable animated image drawables to use nearest sampling when scaling." bug: "370523334" } + +flag { + name: "remove_vri_sketchy_destroy" + namespace: "core_graphics" + description: "Remove the eager yet thread-violating destroyHardwareResources in VRI#die" + bug: "377057106" + metadata { + purpose: PURPOSE_BUGFIX + } +}
\ No newline at end of file diff --git a/libs/hwui/hwui/MinikinUtils.h b/libs/hwui/hwui/MinikinUtils.h index 1510ce1378d8..20acf981d9b9 100644 --- a/libs/hwui/hwui/MinikinUtils.h +++ b/libs/hwui/hwui/MinikinUtils.h @@ -73,7 +73,7 @@ public: static void forFontRun(const minikin::Layout& layout, Paint* paint, F& f) { float saveSkewX = paint->getSkFont().getSkewX(); bool savefakeBold = paint->getSkFont().isEmbolden(); - if (text_feature::typeface_redesign()) { + if (text_feature::typeface_redesign_readonly()) { for (uint32_t runIdx = 0; runIdx < layout.getFontRunCount(); ++runIdx) { uint32_t start = layout.getFontRunStart(runIdx); uint32_t end = layout.getFontRunEnd(runIdx); diff --git a/libs/hwui/jni/BitmapRegionDecoder.cpp b/libs/hwui/jni/BitmapRegionDecoder.cpp index 5ffd5b9016d8..491066b05952 100644 --- a/libs/hwui/jni/BitmapRegionDecoder.cpp +++ b/libs/hwui/jni/BitmapRegionDecoder.cpp @@ -112,9 +112,7 @@ public: return false; } - // Round out the subset so that we decode a slightly larger region, in - // case the subset has fractional components. - SkIRect roundedSubset = desiredSubset.roundOut(); + sampleSize = std::max(sampleSize, 1); // Map the desired subset to the space of the decoded gainmap. The // subset is repositioned relative to the resulting bitmap, and then @@ -123,10 +121,51 @@ public: // for existing gainmap formats. SkRect logicalSubset = desiredSubset.makeOffset(-std::floorf(desiredSubset.left()), -std::floorf(desiredSubset.top())); - logicalSubset.fLeft /= sampleSize; - logicalSubset.fTop /= sampleSize; - logicalSubset.fRight /= sampleSize; - logicalSubset.fBottom /= sampleSize; + logicalSubset = scale(logicalSubset, 1.0f / sampleSize); + + // Round out the subset so that we decode a slightly larger region, in + // case the subset has fractional components. When we round, we need to + // round the downsampled subset to avoid possibly rounding down by accident. + // Consider this concrete example if we round the desired subset directly: + // + // * We are decoding a 18x18 corner of an image + // + // * Gainmap is 1/4 resolution, which is logically a 4.5x4.5 gainmap + // that we would want + // + // * The app wants to downsample by a factor of 2x + // + // * The desired gainmap dimensions are computed to be 3x3 to fit the + // downsampled gainmap, since we need to fill a 2.25x2.25 region that's + // later upscaled to 3x3 + // + // * But, if we round out the desired gainmap region _first_, then we + // request to decode a 5x5 region, downsampled by 2, which actually + // decodes a 2x2 region since skia rounds down internally. But then we transfer + // the result to a 3x3 bitmap using a clipping allocator, which leaves an inset. + // Not only did we get a smaller region than we expected (so, our desired subset is + // not valid), but because the API allows for decoding regions using a recycled + // bitmap, we can't really safely fill in the inset since then we might + // extend the gainmap beyond intended the image bounds. Oops. + // + // * If we instead round out as if we downsampled, then we downsample + // the desired region to 2.25x2.25, round out to 3x3, then upsample back + // into the source gainmap space to get 6x6. Then we decode a 6x6 region + // downsampled into a 3x3 region, and everything's now correct. + // + // Note that we don't always run into this problem, because + // decoders actually round *up* for subsampling when decoding a subset + // that matches the dimensions of the image. E.g., if the original image + // size in the above example was a 20x20 image, so that the gainmap was + // 5x5, then we still manage to downsample into a 3x3 bitmap even with + // the "wrong" math. but that's what we wanted! + // + // Note also that if we overshoot the gainmap bounds with the requested + // subset it isn't a problem either, since now the decoded bitmap is too + // large, rather than too small, so now we can use the desired subset to + // avoid sampling "invalid" colors. + SkRect scaledSubset = scale(desiredSubset, 1.0f / sampleSize); + SkIRect roundedSubset = scale(scaledSubset.roundOut(), static_cast<float>(sampleSize)); RecyclingClippingPixelAllocator allocator(nativeBitmap.get(), false, logicalSubset); if (!mGainmapBRD->decodeRegion(&bm, &allocator, roundedSubset, sampleSize, decodeColorType, @@ -154,7 +193,7 @@ public: const float scaleX = ((float)mGainmapBRD->width()) / mMainImageBRD->width(); const float scaleY = ((float)mGainmapBRD->height()) / mMainImageBRD->height(); - if (uirenderer::Properties::resampleGainmapRegions) { + if (uirenderer::Properties::resampleGainmapRegions()) { const auto srcRect = SkRect::MakeLTRB( mainImageRegion.left() * scaleX, mainImageRegion.top() * scaleY, mainImageRegion.right() * scaleX, mainImageRegion.bottom() * scaleY); @@ -186,6 +225,22 @@ private: , mGainmapBRD(std::move(gainmapBRD)) , mGainmapInfo(info) {} + SkRect scale(SkRect rect, float scale) const { + rect.fLeft *= scale; + rect.fTop *= scale; + rect.fRight *= scale; + rect.fBottom *= scale; + return rect; + } + + SkIRect scale(SkIRect rect, float scale) const { + rect.fLeft *= scale; + rect.fTop *= scale; + rect.fRight *= scale; + rect.fBottom *= scale; + return rect; + } + std::unique_ptr<skia::BitmapRegionDecoder> mMainImageBRD; std::unique_ptr<skia::BitmapRegionDecoder> mGainmapBRD; SkGainmapInfo mGainmapInfo; diff --git a/libs/hwui/jni/Graphics.cpp b/libs/hwui/jni/Graphics.cpp index 258bf91f2124..a210ddf54b2e 100644 --- a/libs/hwui/jni/Graphics.cpp +++ b/libs/hwui/jni/Graphics.cpp @@ -750,7 +750,7 @@ void RecyclingClippingPixelAllocator::copyIfNecessary() { std::optional<SkRect> RecyclingClippingPixelAllocator::getSourceBoundsForUpsample( std::optional<SkRect> subset) { - if (!uirenderer::Properties::resampleGainmapRegions || !subset || subset->isEmpty()) { + if (!uirenderer::Properties::resampleGainmapRegions() || !subset || subset->isEmpty()) { return std::nullopt; } diff --git a/libs/hwui/jni/text/TextShaper.cpp b/libs/hwui/jni/text/TextShaper.cpp index 70e6beda6cb9..5f693462af91 100644 --- a/libs/hwui/jni/text/TextShaper.cpp +++ b/libs/hwui/jni/text/TextShaper.cpp @@ -86,7 +86,7 @@ static jlong shapeTextRun(const uint16_t* text, int textSize, int start, int cou overallDescent = std::max(overallDescent, extent.descent); } - if (text_feature::typeface_redesign()) { + if (text_feature::typeface_redesign_readonly()) { uint32_t runCount = layout.getFontRunCount(); std::unordered_map<minikin::FakedFont, uint32_t, FakedFontKey> fakedToFontIds; @@ -229,7 +229,7 @@ float findValueFromVariationSettings(const minikin::FontFakery& fakery, minikin: // CriticalNative static jfloat TextShaper_Result_getWeightOverride(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint i) { const LayoutWrapper* layout = reinterpret_cast<LayoutWrapper*>(ptr); - if (text_feature::typeface_redesign()) { + if (text_feature::typeface_redesign_readonly()) { float value = findValueFromVariationSettings(layout->layout.getFakery(i), minikin::TAG_wght); return std::isnan(value) ? NO_OVERRIDE : value; @@ -241,7 +241,7 @@ static jfloat TextShaper_Result_getWeightOverride(CRITICAL_JNI_PARAMS_COMMA jlon // CriticalNative static jfloat TextShaper_Result_getItalicOverride(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint i) { const LayoutWrapper* layout = reinterpret_cast<LayoutWrapper*>(ptr); - if (text_feature::typeface_redesign()) { + if (text_feature::typeface_redesign_readonly()) { float value = findValueFromVariationSettings(layout->layout.getFakery(i), minikin::TAG_ital); return std::isnan(value) ? NO_OVERRIDE : value; diff --git a/media/java/android/media/MediaCas.java b/media/java/android/media/MediaCas.java index 88efed55c11f..3f9126aa9456 100644 --- a/media/java/android/media/MediaCas.java +++ b/media/java/android/media/MediaCas.java @@ -1000,7 +1000,10 @@ public final class MediaCas implements AutoCloseable { @SystemApi @RequiresPermission(android.Manifest.permission.TUNER_RESOURCE_ACCESS) public boolean updateResourcePriority(int priority, int niceValue) { - return mTunerResourceManager.updateClientPriority(mClientId, priority, niceValue); + if (mTunerResourceManager != null) { + return mTunerResourceManager.updateClientPriority(mClientId, priority, niceValue); + } + return false; } /** @@ -1017,7 +1020,9 @@ public final class MediaCas implements AutoCloseable { @SystemApi @RequiresPermission(android.Manifest.permission.TUNER_RESOURCE_ACCESS) public void setResourceHolderRetain(boolean resourceHolderRetain) { - mTunerResourceManager.setResourceHolderRetain(mClientId, resourceHolderRetain); + if (mTunerResourceManager != null) { + mTunerResourceManager.setResourceHolderRetain(mClientId, resourceHolderRetain); + } } IHwBinder getBinder() { diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java index e575daeb8d29..2ae89d3300c1 100644 --- a/media/java/android/media/MediaCodec.java +++ b/media/java/android/media/MediaCodec.java @@ -18,6 +18,7 @@ package android.media; import static android.media.codec.Flags.FLAG_NULL_OUTPUT_SURFACE; import static android.media.codec.Flags.FLAG_REGION_OF_INTEREST; +import static android.media.codec.Flags.FLAG_SUBSESSION_METRICS; import static com.android.media.codec.flags.Flags.FLAG_LARGE_AUDIO_FRAME; @@ -890,7 +891,7 @@ import java.util.function.Supplier; any start codes), and submit it as a <strong>regular</strong> input buffer. <p> You will receive an {@link #INFO_OUTPUT_FORMAT_CHANGED} return value from {@link - #dequeueOutputBuffer dequeueOutputBuffer} or a {@link Callback#onOutputBufferAvailable + #dequeueOutputBuffer dequeueOutputBuffer} or a {@link Callback#onOutputFormatChanged onOutputFormatChanged} callback just after the picture-size change takes place and before any frames with the new size have been returned. <p class=note> @@ -1835,6 +1836,13 @@ final public class MediaCodec { private static final int CB_CRYPTO_ERROR = 6; private static final int CB_LARGE_FRAME_OUTPUT_AVAILABLE = 7; + /** + * Callback ID for when the metrics for this codec have been flushed due to + * the start of a new subsession. The associated Java Message object will + * contain the flushed metrics as a PersistentBundle in the obj field. + */ + private static final int CB_METRICS_FLUSHED = 8; + private class EventHandler extends Handler { private MediaCodec mCodec; @@ -2007,6 +2015,15 @@ final public class MediaCodec { break; } + case CB_METRICS_FLUSHED: + { + + if (GetFlag(() -> android.media.codec.Flags.subsessionMetrics())) { + mCallback.onMetricsFlushed(mCodec, (PersistableBundle)msg.obj); + } + break; + } + default: { break; @@ -4958,14 +4975,24 @@ final public class MediaCodec { public native final String getCanonicalName(); /** - * Return Metrics data about the current codec instance. + * Return Metrics data about the current codec instance. + * <p> + * Call this method after configuration, during execution, or after + * the codec has been already stopped. + * <p> + * Beginning with {@link android.os.Build.VERSION_CODES#B} + * this method can be used to get the Metrics data prior to an error. + * (e.g. in {@link Callback#onError} or after a method throws + * {@link MediaCodec.CodecException}.) Before that, the Metrics data was + * cleared on error, resulting in a null return value. * * @return a {@link PersistableBundle} containing the set of attributes and values * available for the media being handled by this instance of MediaCodec * The attributes are descibed in {@link MetricsConstants}. * * Additional vendor-specific fields may also be present in - * the return value. + * the return value. Returns null if there is no Metrics data. + * */ public PersistableBundle getMetrics() { PersistableBundle bundle = native_getMetrics(); @@ -5692,6 +5719,27 @@ final public class MediaCodec { */ public abstract void onOutputFormatChanged( @NonNull MediaCodec codec, @NonNull MediaFormat format); + + /** + * Called when the metrics for this codec have been flushed due to the + * start of a new subsession. + * <p> + * This can happen when the codec is reconfigured after stop(), or + * mid-stream e.g. if the video size changes. When this happens, the + * metrics for the previous subsession are flushed, and + * {@link MediaCodec#getMetrics} will return the metrics for the + * new subsession. This happens just before the {@link Callback#onOutputFormatChanged} + * event, so this <b>optional</b> callback is provided to be able to + * capture the final metrics for the previous subsession. + * + * @param codec The MediaCodec object. + * @param metrics The flushed metrics for this codec. + */ + @FlaggedApi(FLAG_SUBSESSION_METRICS) + public void onMetricsFlushed( + @NonNull MediaCodec codec, @NonNull PersistableBundle metrics) { + // default implementation ignores this callback. + } } private void postEventFromNative( diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java index b08a86ee8f46..bd65b2ecb76a 100644 --- a/media/java/android/media/MediaFormat.java +++ b/media/java/android/media/MediaFormat.java @@ -17,6 +17,7 @@ package android.media; import static android.media.codec.Flags.FLAG_IN_PROCESS_SW_AUDIO_CODEC; +import static android.media.codec.Flags.FLAG_NUM_INPUT_SLOTS; import static android.media.codec.Flags.FLAG_REGION_OF_INTEREST; import static android.media.codec.Flags.FLAG_APV_SUPPORT; @@ -1777,6 +1778,17 @@ public final class MediaFormat { public static final String KEY_SECURITY_MODEL = "security-model"; /** + * A key describing the number of slots used in the codec. When present in input format, + * the associated value indicates the number of input slots. The entry is set by the codec + * if configured with (@link MediaCodec#CONFIGURE_FLAG_BLOCK_MODEL), and will be ignored if set + * by the application. + * <p> + * The associated value is an integer. + */ + @FlaggedApi(FLAG_NUM_INPUT_SLOTS) + public static final String KEY_NUM_SLOTS = "num-slots"; + + /** * QpOffsetRect constitutes the metadata required for encoding a region of interest in an * image or a video frame. The region of interest is represented by a rectangle. The four * integer coordinates of the rectangle are stored in fields left, top, right, bottom. diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java index 3499c438086d..20108e7369d7 100644 --- a/media/java/android/media/MediaRouter2.java +++ b/media/java/android/media/MediaRouter2.java @@ -1771,10 +1771,12 @@ public final class MediaRouter2 { } /** - * A class to control media routing session in media route provider. For example, - * selecting/deselecting/transferring to routes of a session can be done through this. Instances - * are created when {@link TransferCallback#onTransfer(RoutingController, RoutingController)} is - * called, which is invoked after {@link #transferTo(MediaRoute2Info)} is called. + * Controls a media routing session. + * + * <p>Routing controllers wrap a {@link RoutingSessionInfo}, taking care of mapping route ids to + * {@link MediaRoute2Info} instances. You can still access the underlying session using {@link + * #getRoutingSessionInfo()}, but keep in mind it can be changed by other threads. Changes to + * the routing session are notified via {@link ControllerCallback}. */ public class RoutingController { private final Object mControllerLock = new Object(); @@ -1836,7 +1838,9 @@ public final class MediaRouter2 { } /** - * @return the unmodifiable list of currently selected routes + * Returns the unmodifiable list of currently selected routes + * + * @see RoutingSessionInfo#getSelectedRoutes() */ @NonNull public List<MediaRoute2Info> getSelectedRoutes() { @@ -1848,7 +1852,9 @@ public final class MediaRouter2 { } /** - * @return the unmodifiable list of selectable routes for the session. + * Returns the unmodifiable list of selectable routes for the session. + * + * @see RoutingSessionInfo#getSelectableRoutes() */ @NonNull public List<MediaRoute2Info> getSelectableRoutes() { @@ -1860,7 +1866,9 @@ public final class MediaRouter2 { } /** - * @return the unmodifiable list of deselectable routes for the session. + * Returns the unmodifiable list of deselectable routes for the session. + * + * @see RoutingSessionInfo#getDeselectableRoutes() */ @NonNull public List<MediaRoute2Info> getDeselectableRoutes() { diff --git a/media/java/android/media/RoutingSessionInfo.java b/media/java/android/media/RoutingSessionInfo.java index 83a4dd5a682a..3b8cf3fb2909 100644 --- a/media/java/android/media/RoutingSessionInfo.java +++ b/media/java/android/media/RoutingSessionInfo.java @@ -262,7 +262,8 @@ public final class RoutingSessionInfo implements Parcelable { } /** - * Gets the provider id of the session. + * Gets the provider ID of the session. + * * @hide */ @Nullable @@ -271,7 +272,15 @@ public final class RoutingSessionInfo implements Parcelable { } /** - * Gets the list of IDs of selected routes for the session. It shouldn't be empty. + * Gets the list of IDs of selected routes for the session. + * + * <p>Selected routes are the routes that this session is actively routing media to. + * + * <p>The behavior of a routing session with multiple selected routes is ultimately defined by + * the {@link MediaRoute2ProviderService} implementation. However, typically, it's expected that + * all the selected routes of a routing session are playing the same media in sync. + * + * @return A non-empty list of selected route ids. */ @NonNull public List<String> getSelectedRoutes() { @@ -280,6 +289,16 @@ public final class RoutingSessionInfo implements Parcelable { /** * Gets the list of IDs of selectable routes for the session. + * + * <p>Selectable routes can be added to a routing session (via {@link + * MediaRouter2.RoutingController#selectRoute}) in order to add them to the {@link + * #getSelectedRoutes() selected routes}, so that media plays on the newly selected route along + * with the other selected routes. + * + * <p>Not to be confused with {@link #getTransferableRoutes() transferable routes}. Transferring + * to a route makes it the sole selected route. + * + * @return A possibly empty list of selectable route ids. */ @NonNull public List<String> getSelectableRoutes() { @@ -288,6 +307,17 @@ public final class RoutingSessionInfo implements Parcelable { /** * Gets the list of IDs of deselectable routes for the session. + * + * <p>Deselectable routes can be removed from the {@link #getSelectedRoutes() selected routes}, + * so that the routing session stops routing to the newly deselected route, but continues on any + * remaining selected routes. + * + * <p>Deselectable routes should be a subset of the {@link #getSelectedRoutes() selected + * routes}, meaning not all of the selected routes might be deselectable. For example, one of + * the selected routes may be a leader device coordinating group playback, which must always + * remain selected while the session is active. + * + * @return A possibly empty list of deselectable route ids. */ @NonNull public List<String> getDeselectableRoutes() { @@ -296,6 +326,24 @@ public final class RoutingSessionInfo implements Parcelable { /** * Gets the list of IDs of transferable routes for the session. + * + * <p>Transferring to a route (for example, using {@link MediaRouter2#transferTo}) replaces the + * list of {@link #getSelectedRoutes() selected routes} with the target route, causing playback + * to move from one route to another. + * + * <p>Note that this is different from {@link #getSelectableRoutes() selectable routes}, because + * selecting a route makes it part of the selected routes, while transferring to a route makes + * it the selected route. A route can be both transferable and selectable. + * + * <p>Note that playback may transfer across routes without the target route being in the list + * of transferable routes. This can happen by creating a new routing session to the target + * route, and releasing the routing session being transferred from. The difference is that a + * transfer to a route in the transferable list can happen with no intervention from the app, + * with the route provider taking care of the entire operation. A transfer to a route that is + * not in the list of transferable routes (by creating a new session) requires the app to move + * the playback state from one device to the other. + * + * @return A possibly empty list of transferable route ids. */ @NonNull public List<String> getTransferableRoutes() { diff --git a/media/java/android/media/audio/common/AidlConversion.java b/media/java/android/media/audio/common/AidlConversion.java index c1d73f9033cf..8521d1c472a8 100644 --- a/media/java/android/media/audio/common/AidlConversion.java +++ b/media/java/android/media/audio/common/AidlConversion.java @@ -705,6 +705,10 @@ public class AidlConversion { aidl.type = AudioDeviceType.OUT_BROADCAST; aidl.connection = AudioDeviceDescription.CONNECTION_BT_LE; break; + case AudioSystem.DEVICE_OUT_MULTICHANNEL_GROUP: + aidl.type = AudioDeviceType.OUT_MULTICHANNEL_GROUP; + aidl.connection = AudioDeviceDescription.CONNECTION_VIRTUAL; + break; case AudioSystem.DEVICE_IN_BUILTIN_MIC: aidl.type = AudioDeviceType.IN_MICROPHONE; break; diff --git a/media/java/android/media/flags/media_better_together.aconfig b/media/java/android/media/flags/media_better_together.aconfig index 90b4aba690d0..7895eb27b372 100644 --- a/media/java/android/media/flags/media_better_together.aconfig +++ b/media/java/android/media/flags/media_better_together.aconfig @@ -51,7 +51,7 @@ flag { is_exported: true namespace: "media_tv" description: "Enables the following type constant in MediaRoute2Info: LINE_ANALOG, LINE_DIGITAL, AUX_LINE" - bug: "301713440" + bug: "375691732" } flag { @@ -79,7 +79,7 @@ flag { flag { name: "update_client_profile_priority" - namespace: "media" + namespace: "media_solutions" description : "Feature flag to add updateResourcePriority api to MediaCas" bug: "300565729" } @@ -166,3 +166,10 @@ flag { description: "Allows audio input devices routing and volume control via system settings." bug: "355684672" } + +flag { + name: "enable_mirroring_in_media_router_2" + namespace: "media_better_together" + description: "Enables support for mirroring routes in the MediaRouter2 framework, allowing Output Switcher to offer mirroring routes." + bug: "362507305" +} diff --git a/media/java/android/media/quality/AmbientBacklightEvent.java b/media/java/android/media/quality/AmbientBacklightEvent.java index 3bc6b86c0615..5c11def43209 100644 --- a/media/java/android/media/quality/AmbientBacklightEvent.java +++ b/media/java/android/media/quality/AmbientBacklightEvent.java @@ -16,9 +16,11 @@ package android.media.quality; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.media.tv.flags.Flags; import android.os.Parcel; import android.os.Parcelable; @@ -27,8 +29,10 @@ import java.lang.annotation.RetentionPolicy; import java.util.Objects; /** + * Ambient backlight event * @hide */ +@FlaggedApi(Flags.FLAG_MEDIA_QUALITY_FW) public final class AmbientBacklightEvent implements Parcelable { /** @hide */ @@ -64,6 +68,9 @@ public final class AmbientBacklightEvent implements Parcelable { @Nullable private final AmbientBacklightMetadata mMetadata; + /** + * Constructor of AmbientBacklightEvent. + */ public AmbientBacklightEvent(int eventType, @Nullable AmbientBacklightMetadata metadata) { mEventType = eventType; @@ -75,10 +82,19 @@ public final class AmbientBacklightEvent implements Parcelable { mMetadata = in.readParcelable(AmbientBacklightMetadata.class.getClassLoader()); } + /** + * Gets event type. + */ public int getEventType() { return mEventType; } + /** + * Gets ambient backlight metadata. + * + * @return the metadata of the event. It's non-null only for + * {@link #AMBIENT_BACKLIGHT_EVENT_METADATA}. + */ @Nullable public AmbientBacklightMetadata getMetadata() { return mMetadata; @@ -95,7 +111,8 @@ public final class AmbientBacklightEvent implements Parcelable { return 0; } - public static final @NonNull Parcelable.Creator<AmbientBacklightEvent> CREATOR = + @NonNull + public static final Parcelable.Creator<AmbientBacklightEvent> CREATOR = new Parcelable.Creator<AmbientBacklightEvent>() { public AmbientBacklightEvent createFromParcel(Parcel in) { return new AmbientBacklightEvent(in); diff --git a/media/java/android/media/quality/AmbientBacklightMetadata.java b/media/java/android/media/quality/AmbientBacklightMetadata.java index fc779348de11..9c11f9a3e560 100644 --- a/media/java/android/media/quality/AmbientBacklightMetadata.java +++ b/media/java/android/media/quality/AmbientBacklightMetadata.java @@ -16,6 +16,10 @@ package android.media.quality; +import android.annotation.FlaggedApi; +import android.annotation.IntRange; +import android.graphics.PixelFormat; +import android.media.tv.flags.Flags; import android.os.Parcel; import android.os.Parcelable; @@ -24,9 +28,11 @@ import androidx.annotation.NonNull; import java.util.Arrays; /** + * Metadata of ambient backlight. * @hide */ -public class AmbientBacklightMetadata implements Parcelable { +@FlaggedApi(Flags.FLAG_MEDIA_QUALITY_FW) +public final class AmbientBacklightMetadata implements Parcelable { @NonNull private final String mPackageName; private final int mCompressAlgorithm; @@ -37,6 +43,9 @@ public class AmbientBacklightMetadata implements Parcelable { @NonNull private final int[] mZonesColors; + /** + * Constructor of AmbientBacklightMetadata. + */ public AmbientBacklightMetadata(@NonNull String packageName, int compressAlgorithm, int source, int colorFormat, int horizontalZonesNumber, int verticalZonesNumber, @NonNull int[] zonesColors) { @@ -59,31 +68,58 @@ public class AmbientBacklightMetadata implements Parcelable { mZonesColors = in.createIntArray(); } + /** + * Gets package name. + * @hide + */ @NonNull public String getPackageName() { return mPackageName; } + /** + * Gets compress algorithm. + */ + @AmbientBacklightSettings.CompressAlgorithm public int getCompressAlgorithm() { return mCompressAlgorithm; } + /** + * Gets source of ambient backlight detection. + */ + @AmbientBacklightSettings.Source public int getSource() { return mSource; } + /** + * Gets color format. + */ + @PixelFormat.Format public int getColorFormat() { return mColorFormat; } + /** + * Gets the number of lights in each horizontal zone. + */ + @IntRange(from = 0) public int getHorizontalZonesNumber() { return mHorizontalZonesNumber; } + /** + * Gets the number of lights in each vertical zone. + */ + @IntRange(from = 0) public int getVerticalZonesNumber() { return mVerticalZonesNumber; } + /** + * @hide + */ @NonNull public int[] getZonesColors() { return mZonesColors; diff --git a/media/java/android/media/quality/AmbientBacklightSettings.java b/media/java/android/media/quality/AmbientBacklightSettings.java index 391eb225bcab..4ed7bc79fdca 100644 --- a/media/java/android/media/quality/AmbientBacklightSettings.java +++ b/media/java/android/media/quality/AmbientBacklightSettings.java @@ -16,7 +16,11 @@ package android.media.quality; +import android.annotation.FlaggedApi; import android.annotation.IntDef; +import android.annotation.IntRange; +import android.graphics.PixelFormat; +import android.media.tv.flags.Flags; import android.os.Parcel; import android.os.Parcelable; @@ -26,9 +30,11 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** + * Settings for ambient backlight. * @hide */ -public class AmbientBacklightSettings implements Parcelable { +@FlaggedApi(Flags.FLAG_MEDIA_QUALITY_FW) +public final class AmbientBacklightSettings implements Parcelable { /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef({SOURCE_NONE, SOURCE_AUDIO, SOURCE_VIDEO, SOURCE_AUDIO_VIDEO}) @@ -61,6 +67,7 @@ public class AmbientBacklightSettings implements Parcelable { /** * The color format is RGB888. + * @hide */ public static final int COLOR_FORMAT_RGB888 = 1; @@ -75,7 +82,7 @@ public class AmbientBacklightSettings implements Parcelable { public static final int ALGORITHM_NONE = 0; /** - * The compress algorithm is RLE. + * The compress algorithm is run length encoding (RLE). */ public static final int ALGORITHM_RLE = 1; @@ -114,6 +121,9 @@ public class AmbientBacklightSettings implements Parcelable { */ private final int mThreshold; + /** + * Constructs AmbientBacklightSettings. + */ public AmbientBacklightSettings(int source, int maxFps, int colorFormat, int horizontalZonesNumber, int verticalZonesNumber, boolean isLetterboxOmitted, int threshold) { @@ -136,32 +146,57 @@ public class AmbientBacklightSettings implements Parcelable { mThreshold = in.readInt(); } + /** + * Gets source of ambient backlight detection. + */ @Source public int getSource() { return mSource; } + /** + * Gets max frames per second. + */ + @IntRange(from = 1) public int getMaxFps() { return mMaxFps; } - @ColorFormat + /** + * Gets color format. + */ + @PixelFormat.Format public int getColorFormat() { return mColorFormat; } + /** + * Gets the number of lights in each horizontal zone. + */ + @IntRange(from = 0) public int getHorizontalZonesNumber() { return mHorizontalZonesNumber; } + /** + * Gets the number of lights in each vertical zone. + */ + @IntRange(from = 0) public int getVerticalZonesNumber() { return mVerticalZonesNumber; } + /** + * Returns {@code true} if letter box is omitted; {@code false} otherwise. + * @hide + */ public boolean isLetterboxOmitted() { return mIsLetterboxOmitted; } + /** + * @hide + */ public int getThreshold() { return mThreshold; } diff --git a/media/java/android/media/quality/IMediaQualityManager.aidl b/media/java/android/media/quality/IMediaQualityManager.aidl index e6c79dd9681f..250d59b7c2d7 100644 --- a/media/java/android/media/quality/IMediaQualityManager.aidl +++ b/media/java/android/media/quality/IMediaQualityManager.aidl @@ -30,20 +30,22 @@ import android.media.quality.SoundProfile; */ interface IMediaQualityManager { PictureProfile createPictureProfile(in PictureProfile pp); - void updatePictureProfile(in long id, in PictureProfile pp); - void removePictureProfile(in long id); - PictureProfile getPictureProfileById(in long id); + void updatePictureProfile(in String id, in PictureProfile pp); + void removePictureProfile(in String id); + PictureProfile getPictureProfile(in int type, in String name); List<PictureProfile> getPictureProfilesByPackage(in String packageName); List<PictureProfile> getAvailablePictureProfiles(); - List<PictureProfile> getAllPictureProfiles(); + List<String> getPictureProfilePackageNames(); + List<String> getPictureProfileAllowList(); + void setPictureProfileAllowList(in List<String> packages); SoundProfile createSoundProfile(in SoundProfile pp); - void updateSoundProfile(in long id, in SoundProfile pp); - void removeSoundProfile(in long id); - SoundProfile getSoundProfileById(in long id); + void updateSoundProfile(in String id, in SoundProfile pp); + void removeSoundProfile(in String id); + SoundProfile getSoundProfileById(in String id); List<SoundProfile> getSoundProfilesByPackage(in String packageName); List<SoundProfile> getAvailableSoundProfiles(); - List<SoundProfile> getAllSoundProfiles(); + List<String> getSoundProfilePackageNames(); void registerPictureProfileCallback(in IPictureProfileCallback cb); void registerSoundProfileCallback(in ISoundProfileCallback cb); diff --git a/media/java/android/media/quality/IPictureProfileCallback.aidl b/media/java/android/media/quality/IPictureProfileCallback.aidl index 05441cde31e7..34aa2b061caf 100644 --- a/media/java/android/media/quality/IPictureProfileCallback.aidl +++ b/media/java/android/media/quality/IPictureProfileCallback.aidl @@ -17,6 +17,7 @@ package android.media.quality; +import android.media.quality.ParamCapability; import android.media.quality.PictureProfile; /** @@ -24,7 +25,9 @@ import android.media.quality.PictureProfile; * @hide */ oneway interface IPictureProfileCallback { - void onPictureProfileAdded(in long id, in PictureProfile p); - void onPictureProfileUpdated(in long id, in PictureProfile p); - void onPictureProfileRemoved(in long id, in PictureProfile p); + void onPictureProfileAdded(in String id, in PictureProfile p); + void onPictureProfileUpdated(in String id, in PictureProfile p); + void onPictureProfileRemoved(in String id, in PictureProfile p); + void onParamCapabilitiesChanged(in String id, in List<ParamCapability> caps); + void onError(in int err); } diff --git a/media/java/android/media/quality/MediaQualityContract.java b/media/java/android/media/quality/MediaQualityContract.java index 472d7985b304..f07ef873a0af 100644 --- a/media/java/android/media/quality/MediaQualityContract.java +++ b/media/java/android/media/quality/MediaQualityContract.java @@ -33,6 +33,7 @@ public class MediaQualityContract { */ public interface BaseParameters { String PARAMETER_ID = "_id"; + String PARAMETER_TYPE = "_type"; String PARAMETER_NAME = "_name"; String PARAMETER_PACKAGE = "_package"; String PARAMETER_INPUT_ID = "_input_id"; @@ -43,7 +44,7 @@ public class MediaQualityContract { * Parameters picture quality. * @hide */ - public static final class PictureQuality { + public static final class PictureQuality implements BaseParameters { /** * The brightness. * diff --git a/media/java/android/media/quality/MediaQualityManager.java b/media/java/android/media/quality/MediaQualityManager.java index 38a2025535f4..4d4526cf9925 100644 --- a/media/java/android/media/quality/MediaQualityManager.java +++ b/media/java/android/media/quality/MediaQualityManager.java @@ -19,6 +19,7 @@ package android.media.quality; import android.annotation.CallbackExecutor; import android.annotation.FlaggedApi; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.SystemService; import android.content.Context; import android.media.tv.flags.Flags; @@ -63,7 +64,7 @@ public final class MediaQualityManager { mService = service; IPictureProfileCallback ppCallback = new IPictureProfileCallback.Stub() { @Override - public void onPictureProfileAdded(long profileId, PictureProfile profile) { + public void onPictureProfileAdded(String profileId, PictureProfile profile) { synchronized (mLock) { for (PictureProfileCallbackRecord record : mPpCallbackRecords) { // TODO: filter callback record @@ -72,7 +73,7 @@ public final class MediaQualityManager { } } @Override - public void onPictureProfileUpdated(long profileId, PictureProfile profile) { + public void onPictureProfileUpdated(String profileId, PictureProfile profile) { synchronized (mLock) { for (PictureProfileCallbackRecord record : mPpCallbackRecords) { // TODO: filter callback record @@ -81,7 +82,7 @@ public final class MediaQualityManager { } } @Override - public void onPictureProfileRemoved(long profileId, PictureProfile profile) { + public void onPictureProfileRemoved(String profileId, PictureProfile profile) { synchronized (mLock) { for (PictureProfileCallbackRecord record : mPpCallbackRecords) { // TODO: filter callback record @@ -89,6 +90,24 @@ public final class MediaQualityManager { } } } + @Override + public void onParamCapabilitiesChanged(String profileId, List<ParamCapability> caps) { + synchronized (mLock) { + for (PictureProfileCallbackRecord record : mPpCallbackRecords) { + // TODO: filter callback record + record.postParamCapabilitiesChanged(profileId, caps); + } + } + } + @Override + public void onError(int err) { + synchronized (mLock) { + for (PictureProfileCallbackRecord record : mPpCallbackRecords) { + // TODO: filter callback record + record.postError(err); + } + } + } }; ISoundProfileCallback spCallback = new ISoundProfileCallback.Stub() { @Override @@ -175,14 +194,17 @@ public final class MediaQualityManager { /** - * Gets picture profile by given profile ID. - * @return the corresponding picture profile if available; {@code null} if the ID doesn't - * exist or the profile is not accessible to the caller. + * Gets picture profile by given profile type and name. + * + * @return the corresponding picture profile if available; {@code null} if the name doesn't + * exist. * @hide */ - public PictureProfile getPictureProfileById(long profileId) { + @Nullable + public PictureProfile getPictureProfile( + @PictureProfile.ProfileType int type, @NonNull String name) { try { - return mService.getPictureProfileById(profileId); + return mService.getPictureProfile(type, name); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -190,11 +212,13 @@ public final class MediaQualityManager { /** - * @SystemApi gets profiles that available to the given package - * @hide + * Gets profiles that available to the given package. + * + * @hide @SystemApi */ + @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE) - public List<PictureProfile> getPictureProfilesByPackage(String packageName) { + public List<PictureProfile> getPictureProfilesByPackage(@NonNull String packageName) { try { return mService.getPictureProfilesByPackage(packageName); } catch (RemoteException e) { @@ -215,13 +239,16 @@ public final class MediaQualityManager { } /** - * @SystemApi all stored picture profiles of all packages - * @hide + * Gets all package names whose picture profiles are available. + * + * @see #getPictureProfilesByPackage(String) + * @hide @SystemApi */ + @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE) - public List<PictureProfile> getAllPictureProfiles() { + public List<String> getPictureProfilePackageNames() { try { - return mService.getAllPictureProfiles(); + return mService.getPictureProfilePackageNames(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -231,10 +258,12 @@ public final class MediaQualityManager { /** * Creates a picture profile and store it in the system. * - * @return the stored profile with an assigned profile ID. + * @return the stored profile with an assigned profile ID. {@code null} if it's not created + * successfully. * @hide */ - public PictureProfile createPictureProfile(PictureProfile pp) { + @Nullable + public PictureProfile createPictureProfile(@NonNull PictureProfile pp) { try { return mService.createPictureProfile(pp); } catch (RemoteException e) { @@ -247,7 +276,7 @@ public final class MediaQualityManager { * Updates an existing picture profile and store it in the system. * @hide */ - public void updatePictureProfile(long profileId, PictureProfile pp) { + public void updatePictureProfile(@NonNull String profileId, @NonNull PictureProfile pp) { try { mService.updatePictureProfile(profileId, pp); } catch (RemoteException e) { @@ -260,7 +289,7 @@ public final class MediaQualityManager { * Removes a picture profile from the system. * @hide */ - public void removePictureProfile(long profileId) { + public void removePictureProfile(@NonNull String profileId) { try { mService.removePictureProfile(profileId); } catch (RemoteException e) { @@ -307,7 +336,7 @@ public final class MediaQualityManager { * exist or the profile is not accessible to the caller. * @hide */ - public SoundProfile getSoundProfileById(long profileId) { + public SoundProfile getSoundProfileById(String profileId) { try { return mService.getSoundProfileById(profileId); } catch (RemoteException e) { @@ -346,9 +375,9 @@ public final class MediaQualityManager { * @hide */ @RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE) - public List<SoundProfile> getAllSoundProfiles() { + public List<String> getSoundProfilePackageNames() { try { - return mService.getAllSoundProfiles(); + return mService.getSoundProfilePackageNames(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -374,7 +403,7 @@ public final class MediaQualityManager { * Updates an existing sound profile and store it in the system. * @hide */ - public void updateSoundProfile(long profileId, SoundProfile sp) { + public void updateSoundProfile(String profileId, SoundProfile sp) { try { mService.updateSoundProfile(profileId, sp); } catch (RemoteException e) { @@ -387,7 +416,7 @@ public final class MediaQualityManager { * Removes a sound profile from the system. * @hide */ - public void removeSoundProfile(long profileId) { + public void removeSoundProfile(String profileId) { try { mService.removeSoundProfile(profileId); } catch (RemoteException e) { @@ -399,7 +428,8 @@ public final class MediaQualityManager { * Gets capability information of the given parameters. * @hide */ - public List<ParamCapability> getParamCapabilities(List<String> names) { + @NonNull + public List<ParamCapability> getParamCapabilities(@NonNull List<String> names) { try { return mService.getParamCapabilities(names); } catch (RemoteException e) { @@ -408,7 +438,38 @@ public final class MediaQualityManager { } /** + * Gets the allowlist of packages that can create and removed picture profiles + * + * @see #createPictureProfile(PictureProfile) + * @see #removePictureProfile(String) + * @hide + */ + @RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE) + @NonNull + public List<String> getPictureProfileAllowList() { + try { + return mService.getPictureProfileAllowList(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Sets the allowlist of packages that can create and removed picture profiles + * @hide + */ + @RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE) + public void setPictureProfileAllowList(@NonNull List<String> packageNames) { + try { + mService.setPictureProfileAllowList(packageNames); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Returns {@code true} if media quality HAL is implemented; {@code false} otherwise. + * @hide */ public boolean isSupported() { try { @@ -506,7 +567,6 @@ public final class MediaQualityManager { /** * Registers a {@link AmbientBacklightCallback}. - * @hide */ public void registerAmbientBacklightCallback( @NonNull @CallbackExecutor Executor executor, @@ -520,7 +580,6 @@ public final class MediaQualityManager { /** * Unregisters the existing {@link AmbientBacklightCallback}. - * @hide */ public void unregisterAmbientBacklightCallback( @NonNull final AmbientBacklightCallback callback) { @@ -541,7 +600,6 @@ public final class MediaQualityManager { * Set the ambient backlight settings. * * @param settings The settings to use for the backlight detector. - * @hide */ public void setAmbientBacklightSettings( @NonNull AmbientBacklightSettings settings) { @@ -557,7 +615,6 @@ public final class MediaQualityManager { * Enables or disables the ambient backlight detection. * * @param enabled {@code true} to enable, {@code false} to disable. - * @hide */ public void setAmbientBacklightEnabled(boolean enabled) { try { @@ -581,7 +638,7 @@ public final class MediaQualityManager { return mCallback; } - public void postPictureProfileAdded(final long id, PictureProfile profile) { + public void postPictureProfileAdded(final String id, PictureProfile profile) { mExecutor.execute(new Runnable() { @Override @@ -591,7 +648,7 @@ public final class MediaQualityManager { }); } - public void postPictureProfileUpdated(final long id, PictureProfile profile) { + public void postPictureProfileUpdated(final String id, PictureProfile profile) { mExecutor.execute(new Runnable() { @Override public void run() { @@ -600,7 +657,7 @@ public final class MediaQualityManager { }); } - public void postPictureProfileRemoved(final long id, PictureProfile profile) { + public void postPictureProfileRemoved(final String id, PictureProfile profile) { mExecutor.execute(new Runnable() { @Override public void run() { @@ -608,6 +665,24 @@ public final class MediaQualityManager { } }); } + + public void postParamCapabilitiesChanged(final String id, List<ParamCapability> caps) { + mExecutor.execute(new Runnable() { + @Override + public void run() { + mCallback.onParamCapabilitiesChanged(id, caps); + } + }); + } + + public void postError(int error) { + mExecutor.execute(new Runnable() { + @Override + public void run() { + mCallback.onError(error); + } + }); + } } private static final class SoundProfileCallbackRecord { @@ -681,24 +756,57 @@ public final class MediaQualityManager { */ public abstract static class PictureProfileCallback { /** + * This is invoked when a picture profile has been added. + * + * @param profileId the ID of the profile. + * @param profile the newly added profile. * @hide */ - public void onPictureProfileAdded(long id, PictureProfile profile) { + public void onPictureProfileAdded( + @NonNull String profileId, @NonNull PictureProfile profile) { } + /** + * This is invoked when a picture profile has been updated. + * + * @param profileId the ID of the profile. + * @param profile the profile with updated info. * @hide */ - public void onPictureProfileUpdated(long id, PictureProfile profile) { + public void onPictureProfileUpdated( + @NonNull String profileId, @NonNull PictureProfile profile) { } + /** + * This is invoked when a picture profile has been removed. + * + * @param profileId the ID of the profile. + * @param profile the removed profile. * @hide */ - public void onPictureProfileRemoved(long id, PictureProfile profile) { + public void onPictureProfileRemoved( + @NonNull String profileId, @NonNull PictureProfile profile) { } + /** + * This is invoked when an issue has occurred. + * + * @param errorCode the error code * @hide */ - public void onError(int errorCode) { + public void onError(@PictureProfile.ErrorCode int errorCode) { + } + + /** + * This is invoked when parameter capabilities has been changed due to status changes of the + * content. + * + * @param profileId the ID of the profile used by the media content. + * @param updatedCaps the updated capabilities. + * @hide + */ + public void onParamCapabilitiesChanged( + @NonNull String profileId, @NonNull List<ParamCapability> updatedCaps) { } } @@ -731,14 +839,12 @@ public final class MediaQualityManager { /** * Callback used to monitor status of ambient backlight. - * @hide */ public abstract static class AmbientBacklightCallback { /** * Called when new ambient backlight event is emitted. - * @hide */ - public void onAmbientBacklightEvent(AmbientBacklightEvent event) { + public void onAmbientBacklightEvent(@NonNull AmbientBacklightEvent event) { } } } diff --git a/media/java/android/media/quality/ParamCapability.java b/media/java/android/media/quality/ParamCapability.java index 70e85920c12f..0b698a9c1ad2 100644 --- a/media/java/android/media/quality/ParamCapability.java +++ b/media/java/android/media/quality/ParamCapability.java @@ -34,7 +34,7 @@ import java.lang.annotation.RetentionPolicy; * @hide */ @FlaggedApi(Flags.FLAG_MEDIA_QUALITY_FW) -public class ParamCapability implements Parcelable { +public final class ParamCapability implements Parcelable { /** @hide */ @IntDef(flag = true, prefix = { "TYPE_" }, value = { @@ -104,6 +104,7 @@ public class ParamCapability implements Parcelable { @NonNull private final Bundle mCaps; + /** @hide */ protected ParamCapability(Parcel in) { mName = in.readString(); mIsSupported = in.readBoolean(); @@ -112,7 +113,7 @@ public class ParamCapability implements Parcelable { } @Override - public void writeToParcel(Parcel dest, int flags) { + public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeString(mName); dest.writeBoolean(mIsSupported); dest.writeInt(mType); @@ -124,6 +125,7 @@ public class ParamCapability implements Parcelable { return 0; } + @NonNull public static final Creator<ParamCapability> CREATOR = new Creator<ParamCapability>() { @Override public ParamCapability createFromParcel(Parcel in) { diff --git a/media/java/android/media/quality/PictureProfile.java b/media/java/android/media/quality/PictureProfile.java index 8fb57124d33e..2be47dd87ef2 100644 --- a/media/java/android/media/quality/PictureProfile.java +++ b/media/java/android/media/quality/PictureProfile.java @@ -71,6 +71,53 @@ public final class PictureProfile implements Parcelable { */ public static final int TYPE_APPLICATION = 2; + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(flag = false, prefix = "ERROR_", value = { + ERROR_UNKNOWN, + ERROR_NO_PERMISSION, + ERROR_DUPLICATE, + ERROR_INVALID_ARGUMENT, + ERROR_NOT_ALLOWLISTED + }) + public @interface ErrorCode {} + + /** + * Error code for unknown errors. + * @hide + */ + public static final int ERROR_UNKNOWN = 0; + + /** + * Error code for missing necessary permission to handle the profiles. + * @hide + */ + public static final int ERROR_NO_PERMISSION = 1; + + /** + * Error code for creating a profile with existing profile type and name. + * + * @see #getProfileType() + * @see #getName() + * @hide + */ + public static final int ERROR_DUPLICATE = 2; + + /** + * Error code for invalid argument. + * @hide + */ + public static final int ERROR_INVALID_ARGUMENT = 3; + + /** + * Error code for the case when an operation requires an allowlist but the caller is not in the + * list. + * + * @see MediaQualityManager#getPictureProfileAllowList() + * @hide + */ + public static final int ERROR_NOT_ALLOWLISTED = 4; + private PictureProfile(@NonNull Parcel in) { mId = in.readString(); diff --git a/media/java/android/media/quality/PictureProfileHandle.aidl b/media/java/android/media/quality/PictureProfileHandle.aidl new file mode 100644 index 000000000000..5d14631dbb73 --- /dev/null +++ b/media/java/android/media/quality/PictureProfileHandle.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.quality; + +parcelable PictureProfileHandle; diff --git a/media/java/android/media/quality/PictureProfileHandle.java b/media/java/android/media/quality/PictureProfileHandle.java new file mode 100644 index 000000000000..2b1cda4eb742 --- /dev/null +++ b/media/java/android/media/quality/PictureProfileHandle.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.quality; + +import android.annotation.FlaggedApi; +import android.os.Parcel; +import android.os.Parcelable; + +import androidx.annotation.NonNull; + +// TODO(b/337330263): Expose as public API after API review +/** + * A type-safe handle to a picture profile, which represents a collection of parameters used to + * configure picture processing hardware to enhance the quality of graphic buffers. + * @hide + */ +@FlaggedApi(android.media.tv.flags.Flags.FLAG_MEDIA_QUALITY_FW) +public final class PictureProfileHandle implements Parcelable { + private final long mId; + + @FlaggedApi(android.media.tv.flags.Flags.FLAG_MEDIA_QUALITY_FW) + public PictureProfileHandle(long id) { + mId = id; + } + + @FlaggedApi(android.media.tv.flags.Flags.FLAG_MEDIA_QUALITY_FW) + public long getId() { + return mId; + } + + @FlaggedApi(android.media.tv.flags.Flags.FLAG_MEDIA_QUALITY_FW) + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeLong(mId); + } + + @FlaggedApi(android.media.tv.flags.Flags.FLAG_MEDIA_QUALITY_FW) + @Override + public int describeContents() { + return 0; + } + + @FlaggedApi(android.media.tv.flags.Flags.FLAG_MEDIA_QUALITY_FW) + @NonNull + public static final Creator<PictureProfileHandle> CREATOR = + new Creator<PictureProfileHandle>() { + @Override + public PictureProfileHandle createFromParcel(Parcel in) { + return new PictureProfileHandle(in); + } + + @Override + public PictureProfileHandle[] newArray(int size) { + return new PictureProfileHandle[size]; + } + }; + + private PictureProfileHandle(@NonNull Parcel in) { + mId = in.readLong(); + } +} diff --git a/media/java/android/media/tv/TvInputServiceExtensionManager.java b/media/java/android/media/tv/TvInputServiceExtensionManager.java index c514f6ed04ef..9442726508c6 100644 --- a/media/java/android/media/tv/TvInputServiceExtensionManager.java +++ b/media/java/android/media/tv/TvInputServiceExtensionManager.java @@ -20,7 +20,9 @@ import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.annotation.StringDef; +import android.annotation.SystemApi; import android.media.tv.flags.Flags; import android.os.IBinder; import android.os.RemoteException; @@ -43,6 +45,7 @@ import java.util.Set; * * @hide */ +@SystemApi @FlaggedApi(Flags.FLAG_TIF_EXTENSION_STANDARDIZATION) public final class TvInputServiceExtensionManager { private static final String TAG = "TvInputServiceExtensionManager"; @@ -186,8 +189,7 @@ public final class TvInputServiceExtensionManager { @Retention(RetentionPolicy.SOURCE) public @interface StandardizedExtensionName {} /** - * Interface responsible for creating scan session and obtain parameters. - * @hide + * Interface responsible for creating scan session and obtaining related parameters. */ public static final String ISCAN_INTERFACE = SCAN_PACKAGE + "IScanInterface"; /** @@ -286,12 +288,10 @@ public final class TvInputServiceExtensionManager { public static final String ISCAN_SAT_SEARCH = SCAN_PACKAGE + "IScanSatSearch"; /** * Interface for Over-the-Air Download. - * @hide */ public static final String IOAD_UPDATE_INTERFACE = OAD_PACKAGE + "IOadUpdateInterface"; /** * Interface for handling conditional access module app related information. - * @hide */ public static final String ICAM_APP_INFO_SERVICE = CAM_PACKAGE + "ICamAppInfoService"; /** @@ -406,8 +406,7 @@ public final class TvInputServiceExtensionManager { public static final String IDOWNLOADABLE_RATING_TABLE_MONITOR = RATING_PACKAGE + "IDownloadableRatingTableMonitor"; /** - * Interface for handling RRT rating related information. - * @hide + * Interface for handling Region Rating Table rating system related information. */ public static final String IRATING_INTERFACE = RATING_PACKAGE + "IRatingInterface"; /** @@ -442,12 +441,10 @@ public final class TvInputServiceExtensionManager { public static final String IPROGRAM_INFO_LISTENER = RATING_PACKAGE + "IProgramInfoListener"; /** * Interface for getting broadcast time related information. - * @hide */ public static final String IBROADCAST_TIME = TIME_PACKAGE + "BroadcastTime"; /** * Interface for handling data service signal information on teletext. - * @hide */ public static final String IDATA_SERVICE_SIGNAL_INFO = TELETEXT_PACKAGE + "IDataServiceSignalInfo"; @@ -476,17 +473,14 @@ public final class TvInputServiceExtensionManager { + "IScanBackgroundServiceUpdateListener"; /** * Interface for generating client token. - * @hide */ public static final String ICLIENT_TOKEN = CLIENT_TOKEN_PACKAGE + "IClientToken"; /** * Interfaces for handling screen mode information. - * @hide */ public static final String ISCREEN_MODE_SETTINGS = SCREEN_MODE_PACKAGE + "IScreenModeSettings"; /** * Interfaces for handling HDMI signal information update. - * @hide */ public static final String IHDMI_SIGNAL_INTERFACE = SIGNAL_PACKAGE + "IHdmiSignalInterface"; /** @@ -529,7 +523,6 @@ public final class TvInputServiceExtensionManager { public static final String ISERVICE_LIST_EDIT = SERVICE_DATABASE_PACKAGE + "IServiceListEdit"; /** * Interfaces for changes on service database updates. - * @hide */ public static final String ISERVICE_LIST_EDIT_LISTENER = SERVICE_DATABASE_PACKAGE + "IServiceListEditListener"; @@ -587,8 +580,7 @@ public final class TvInputServiceExtensionManager { public static final String ICHANNEL_LIST_TRANSFER = SERVICE_DATABASE_PACKAGE + "IChannelListTransfer"; /** - * Interfaces for record contents updates. - * @hide + * Interface for operations related to recorded contents. */ public static final String IRECORDED_CONTENTS = PVR_PACKAGE + "IRecordedContents"; /** @@ -605,7 +597,6 @@ public final class TvInputServiceExtensionManager { + "IGetInfoRecordedContentsCallback"; /** * Interfaces for monitoring present event information. - * @hide */ public static final String IEVENT_MONITOR = EVENT_PACKAGE + "IEventMonitor"; /** @@ -784,20 +775,22 @@ public final class TvInputServiceExtensionManager { } /** - * This function should be used by OEM to register IBinder objects that implement - * standardized AIDL interfaces. + * Registers IBinder objects that implement standardized AIDL interfaces. + * <p>This function should be used by SoCs/OEMs * * @param extensionName Extension Interface Name * @param binder IBinder object to be registered - * @return REGISTER_SUCCESS on success of registering IBinder object - * REGISTER_FAIL_NAME_NOT_STANDARDIZED on failure due to registering extension with - * non-standardized name - * REGISTER_FAIL_IMPLEMENTATION_NOT_STANDARDIZED on failure due to IBinder not + * @return {@link #REGISTER_SUCCESS} on success of registering IBinder object + * {@link #REGISTER_FAIL_NAME_NOT_STANDARDIZED} on failure due to registering extension + * with non-standardized name + * {@link #REGISTER_FAIL_IMPLEMENTATION_NOT_STANDARDIZED} on failure due to IBinder not * implementing standardized AIDL interface - * REGISTER_FAIL_REMOTE_EXCEPTION on failure due to remote exception + * {@link #REGISTER_FAIL_REMOTE_EXCEPTION} on failure due to remote exception * * @hide */ + @SystemApi + @RequiresPermission(android.Manifest.permission.TV_INPUT_HARDWARE) @RegisterResult public int registerExtensionIBinder(@StandardizedExtensionName @NonNull String extensionName, @NonNull IBinder binder) { diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp index 4492c858c084..001653b08f0c 100644 --- a/media/jni/android_media_MediaCodec.cpp +++ b/media/jni/android_media_MediaCodec.cpp @@ -1461,6 +1461,21 @@ void JMediaCodec::handleCallback(const sp<AMessage> &msg) { break; } + case MediaCodec::CB_METRICS_FLUSHED: + { + sp<WrapperObject<std::unique_ptr<mediametrics::Item>>> metrics; + CHECK(msg->findObject("metrics", (sp<RefBase>*)&metrics)); + + // metrics should never be null. Not sure if checking it here adds any value. + if (metrics == nullptr) { + return; + } + + mediametrics::Item *item = metrics->value.get(); + obj = MediaMetricsJNI::writeMetricsToBundle(env, item, NULL); + break; + } + default: TRESPASS(); } diff --git a/media/tests/aidltests/src/com/android/media/AidlConversionUnitTests.java b/media/tests/aidltests/src/com/android/media/AidlConversionUnitTests.java index 09573909c288..d9a1221e529c 100644 --- a/media/tests/aidltests/src/com/android/media/AidlConversionUnitTests.java +++ b/media/tests/aidltests/src/com/android/media/AidlConversionUnitTests.java @@ -18,6 +18,7 @@ package android.media.audio.common; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; @@ -504,6 +505,27 @@ public final class AidlConversionUnitTests { assertEquals(AudioDeviceType.OUT_DEVICE, port.ext.getDevice().device.type.type); } + @Test + public void testAudioDeviceDescriptionConversion() { + for (int nativeDeviceType : AudioSystem.DEVICE_OUT_ALL_SET) { + assertNotEquals( + AidlConversion.api2aidl_NativeType_AudioDeviceDescription(nativeDeviceType) + .type, + AudioDeviceType.NONE); + } + + for (int nativeDeviceType : AudioSystem.DEVICE_IN_ALL_SET) { + if (nativeDeviceType == AudioSystem.DEVICE_IN_COMMUNICATION + || nativeDeviceType == AudioSystem.DEVICE_IN_AMBIENT) { + continue; + } + assertNotEquals( + AidlConversion.api2aidl_NativeType_AudioDeviceDescription(nativeDeviceType) + .type, + AudioDeviceType.NONE); + } + } + private static AudioFormatDescription createPcm16FormatAidl() { final AudioFormatDescription aidl = new AudioFormatDescription(); aidl.type = AudioFormatType.PCM; diff --git a/native/android/Android.bp b/native/android/Android.bp index 3eb99c3387f7..da29c49f9d7b 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", + "dynamic_instrumentation_manager.cpp", "hardware_buffer_jni.cpp", "input.cpp", "input_transfer_token.cpp", @@ -100,6 +101,7 @@ cc_library_shared { "android.hardware.configstore@1.0", "android.hardware.configstore-utils", "android.os.flags-aconfig-cc", + "dynamic_instrumentation_manager_aidl-cpp", "libnativedisplay", "libfmq", ], diff --git a/native/android/dynamic_instrumentation_manager.cpp b/native/android/dynamic_instrumentation_manager.cpp new file mode 100644 index 000000000000..d9bacb116f96 --- /dev/null +++ b/native/android/dynamic_instrumentation_manager.cpp @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "ADynamicInstrumentationManager" +#include <android/dynamic_instrumentation_manager.h> +#include <android/os/instrumentation/ExecutableMethodFileOffsets.h> +#include <android/os/instrumentation/IDynamicInstrumentationManager.h> +#include <android/os/instrumentation/MethodDescriptor.h> +#include <android/os/instrumentation/TargetProcess.h> +#include <binder/Binder.h> +#include <binder/IServiceManager.h> +#include <utils/Log.h> + +#include <mutex> +#include <optional> +#include <string> +#include <vector> + +namespace android::dynamicinstrumentationmanager { + +// Global instance of IDynamicInstrumentationManager, service is obtained only on first use. +static std::mutex mLock; +static sp<os::instrumentation::IDynamicInstrumentationManager> mService; + +sp<os::instrumentation::IDynamicInstrumentationManager> getService() { + std::lock_guard<std::mutex> scoped_lock(mLock); + if (mService == nullptr || !IInterface::asBinder(mService)->isBinderAlive()) { + sp<IBinder> binder = + defaultServiceManager()->waitForService(String16("dynamic_instrumentation")); + mService = interface_cast<os::instrumentation::IDynamicInstrumentationManager>(binder); + } + return mService; +} + +} // namespace android::dynamicinstrumentationmanager + +using namespace android; +using namespace dynamicinstrumentationmanager; + +struct ADynamicInstrumentationManager_TargetProcess { + uid_t uid; + uid_t pid; + std::string processName; + + ADynamicInstrumentationManager_TargetProcess(uid_t uid, pid_t pid, const char* processName) + : uid(uid), pid(pid), processName(processName) {} +}; + +ADynamicInstrumentationManager_TargetProcess* ADynamicInstrumentationManager_TargetProcess_create( + uid_t uid, pid_t pid, const char* processName) { + return new ADynamicInstrumentationManager_TargetProcess(uid, pid, processName); +} + +void ADynamicInstrumentationManager_TargetProcess_destroy( + ADynamicInstrumentationManager_TargetProcess* instance) { + delete instance; +} + +struct ADynamicInstrumentationManager_MethodDescriptor { + std::string fqcn; + std::string methodName; + std::vector<std::string> fqParameters; + + ADynamicInstrumentationManager_MethodDescriptor(const char* fqcn, const char* methodName, + const char* fullyQualifiedParameters[], + size_t numParameters) + : fqcn(fqcn), methodName(methodName) { + std::vector<std::string> fqParameters; + fqParameters.reserve(numParameters); + std::copy_n(fullyQualifiedParameters, numParameters, std::back_inserter(fqParameters)); + this->fqParameters = std::move(fqParameters); + } +}; + +ADynamicInstrumentationManager_MethodDescriptor* +ADynamicInstrumentationManager_MethodDescriptor_create(const char* fullyQualifiedClassName, + const char* methodName, + const char* fullyQualifiedParameters[], + size_t numParameters) { + return new ADynamicInstrumentationManager_MethodDescriptor(fullyQualifiedClassName, methodName, + fullyQualifiedParameters, + numParameters); +} + +void ADynamicInstrumentationManager_MethodDescriptor_destroy( + ADynamicInstrumentationManager_MethodDescriptor* instance) { + delete instance; +} + +struct ADynamicInstrumentationManager_ExecutableMethodFileOffsets { + std::string containerPath; + uint64_t containerOffset; + uint64_t methodOffset; +}; + +ADynamicInstrumentationManager_ExecutableMethodFileOffsets* +ADynamicInstrumentationManager_ExecutableMethodFileOffsets_create() { + return new ADynamicInstrumentationManager_ExecutableMethodFileOffsets(); +} + +const char* ADynamicInstrumentationManager_ExecutableMethodFileOffsets_getContainerPath( + ADynamicInstrumentationManager_ExecutableMethodFileOffsets* instance) { + return instance->containerPath.c_str(); +} + +uint64_t ADynamicInstrumentationManager_ExecutableMethodFileOffsets_getContainerOffset( + ADynamicInstrumentationManager_ExecutableMethodFileOffsets* instance) { + return instance->containerOffset; +} + +uint64_t ADynamicInstrumentationManager_ExecutableMethodFileOffsets_getMethodOffset( + ADynamicInstrumentationManager_ExecutableMethodFileOffsets* instance) { + return instance->methodOffset; +} + +void ADynamicInstrumentationManager_ExecutableMethodFileOffsets_destroy( + ADynamicInstrumentationManager_ExecutableMethodFileOffsets* instance) { + delete instance; +} + +int32_t ADynamicInstrumentationManager_getExecutableMethodFileOffsets( + const ADynamicInstrumentationManager_TargetProcess* targetProcess, + const ADynamicInstrumentationManager_MethodDescriptor* methodDescriptor, + ADynamicInstrumentationManager_ExecutableMethodFileOffsets** out) { + android::os::instrumentation::TargetProcess targetProcessParcel; + targetProcessParcel.uid = targetProcess->uid; + targetProcessParcel.pid = targetProcess->pid; + targetProcessParcel.processName = targetProcess->processName; + + android::os::instrumentation::MethodDescriptor methodDescriptorParcel; + methodDescriptorParcel.fullyQualifiedClassName = methodDescriptor->fqcn; + methodDescriptorParcel.methodName = methodDescriptor->methodName; + methodDescriptorParcel.fullyQualifiedParameters = methodDescriptor->fqParameters; + + sp<os::instrumentation::IDynamicInstrumentationManager> service = getService(); + if (service == nullptr) { + return INVALID_OPERATION; + } + + std::optional<android::os::instrumentation::ExecutableMethodFileOffsets> offsets; + binder_status_t result = + service->getExecutableMethodFileOffsets(targetProcessParcel, methodDescriptorParcel, + &offsets) + .exceptionCode(); + if (result != OK) { + return result; + } + + if (offsets != std::nullopt) { + auto* value = new ADynamicInstrumentationManager_ExecutableMethodFileOffsets(); + value->containerPath = offsets->containerPath; + value->containerOffset = offsets->containerOffset; + value->methodOffset = offsets->methodOffset; + *out = value; + } else { + *out = nullptr; + } + + return result; +}
\ No newline at end of file diff --git a/native/android/include_platform/android/dynamic_instrumentation_manager.h b/native/android/include_platform/android/dynamic_instrumentation_manager.h new file mode 100644 index 000000000000..6c46288954bf --- /dev/null +++ b/native/android/include_platform/android/dynamic_instrumentation_manager.h @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __ADYNAMICINSTRUMENTATIONMANAGER_H__ +#define __ADYNAMICINSTRUMENTATIONMANAGER_H__ + +#include <sys/cdefs.h> +#include <sys/types.h> + +__BEGIN_DECLS + +struct ADynamicInstrumentationManager_MethodDescriptor; +typedef struct ADynamicInstrumentationManager_MethodDescriptor + ADynamicInstrumentationManager_MethodDescriptor; + +struct ADynamicInstrumentationManager_TargetProcess; +typedef struct ADynamicInstrumentationManager_TargetProcess + ADynamicInstrumentationManager_TargetProcess; + +struct ADynamicInstrumentationManager_ExecutableMethodFileOffsets; +typedef struct ADynamicInstrumentationManager_ExecutableMethodFileOffsets + ADynamicInstrumentationManager_ExecutableMethodFileOffsets; + +/** + * Initializes an ADynamicInstrumentationManager_TargetProcess. Caller must clean up when they are + * done with ADynamicInstrumentationManager_TargetProcess_destroy. + * + * @param uid of targeted process. + * @param pid of targeted process. + * @param processName to disambiguate from corner cases that may arise from pid reuse. + */ +ADynamicInstrumentationManager_TargetProcess* _Nonnull + ADynamicInstrumentationManager_TargetProcess_create( + uid_t uid, pid_t pid, const char* _Nonnull processName) __INTRODUCED_IN(36); +/** + * Clean up an ADynamicInstrumentationManager_TargetProcess. + * + * @param instance returned from ADynamicInstrumentationManager_TargetProcess_create. + */ +void ADynamicInstrumentationManager_TargetProcess_destroy( + ADynamicInstrumentationManager_TargetProcess* _Nonnull instance) __INTRODUCED_IN(36); + +/** + * Initializes an ADynamicInstrumentationManager_MethodDescriptor. Caller must clean up when they + * are done with ADynamicInstrumentationManager_MethodDescriptor_Destroy. + * + * @param fullyQualifiedClassName fqcn of class containing the method. + * @param methodName + * @param fullyQualifiedParameters fqcn of parameters of the method's signature, or e.g. "int" for + * primitives. + * @param numParameters length of `fullyQualifiedParameters` array. + */ +ADynamicInstrumentationManager_MethodDescriptor* _Nonnull + ADynamicInstrumentationManager_MethodDescriptor_create( + const char* _Nonnull fullyQualifiedClassName, const char* _Nonnull methodName, + const char* _Nonnull fullyQualifiedParameters[_Nonnull], size_t numParameters) + __INTRODUCED_IN(36); +/** + * Clean up an ADynamicInstrumentationManager_MethodDescriptor. + * + * @param instance returned from ADynamicInstrumentationManager_MethodDescriptor_create. + */ +void ADynamicInstrumentationManager_MethodDescriptor_destroy( + ADynamicInstrumentationManager_MethodDescriptor* _Nonnull instance) __INTRODUCED_IN(36); + +/** + * Get the containerPath calculated by + * ADynamicInstrumentationManager_getExecutableMethodFileOffsets. + * @param instance created with ADynamicInstrumentationManager_getExecutableMethodFileOffsets. + * @return The OS path of the containing file. + */ +const char* _Nullable ADynamicInstrumentationManager_ExecutableMethodFileOffsets_getContainerPath( + ADynamicInstrumentationManager_ExecutableMethodFileOffsets* _Nonnull instance) + __INTRODUCED_IN(36); +/** + * Get the containerOffset calculated by + * ADynamicInstrumentationManager_getExecutableMethodFileOffsets. + * @param instance created with ADynamicInstrumentationManager_getExecutableMethodFileOffsets. + * @return The offset of the containing file within the process' memory. + */ +uint64_t ADynamicInstrumentationManager_ExecutableMethodFileOffsets_getContainerOffset( + ADynamicInstrumentationManager_ExecutableMethodFileOffsets* _Nonnull instance) + __INTRODUCED_IN(36); +/** + * Get the methodOffset calculated by ADynamicInstrumentationManager_getExecutableMethodFileOffsets. + * @param instance created with ADynamicInstrumentationManager_getExecutableMethodFileOffsets. + * @return The offset of the method within the containing file. + */ +uint64_t ADynamicInstrumentationManager_ExecutableMethodFileOffsets_getMethodOffset( + ADynamicInstrumentationManager_ExecutableMethodFileOffsets* _Nonnull instance) + __INTRODUCED_IN(36); +/** + * Clean up an ADynamicInstrumentationManager_ExecutableMethodFileOffsets. + * + * @param instance returned from ADynamicInstrumentationManager_getExecutableMethodFileOffsets. + */ +void ADynamicInstrumentationManager_ExecutableMethodFileOffsets_destroy( + ADynamicInstrumentationManager_ExecutableMethodFileOffsets* _Nonnull instance) + __INTRODUCED_IN(36); +/** + * Provides ART metadata about the described java method within the target process. + * + * @param targetProcess describes for which process the data is requested. + * @param methodDescriptor describes the targeted method. + * @param out will be populated with the data if successful. A nullptr combined + * with an OK status means that the program method is defined, but the offset + * info was unavailable because it is not AOT compiled. + * @return status indicating success or failure. The values correspond to the `binder_exception_t` + * enum values from <android/binder_status.h>. + */ +int32_t ADynamicInstrumentationManager_getExecutableMethodFileOffsets( + const ADynamicInstrumentationManager_TargetProcess* _Nonnull targetProcess, + const ADynamicInstrumentationManager_MethodDescriptor* _Nonnull methodDescriptor, + ADynamicInstrumentationManager_ExecutableMethodFileOffsets* _Nonnull* _Nullable out) + __INTRODUCED_IN(36); + +__END_DECLS + +#endif // __ADYNAMICINSTRUMENTATIONMANAGER_H__ diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt index b025cb880ee7..a0460572abfc 100644 --- a/native/android/libandroid.map.txt +++ b/native/android/libandroid.map.txt @@ -4,6 +4,15 @@ LIBANDROID { AActivityManager_removeUidImportanceListener; # systemapi introduced=31 AActivityManager_isUidActive; # systemapi introduced=31 AActivityManager_getUidImportance; # systemapi introduced=31 + ADynamicInstrumentationManager_TargetProcess_create; # systemapi + ADynamicInstrumentationManager_TargetProcess_destroy; # systemapi + ADynamicInstrumentationManager_MethodDescriptor_create; # systemapi + ADynamicInstrumentationManager_MethodDescriptor_destroy; # systemapi + ADynamicInstrumentationManager_getExecutableMethodFileOffsets; # systemapi + ADynamicInstrumentationManager_ExecutableMethodFileOffsets_getContainerPath; # systemapi + ADynamicInstrumentationManager_ExecutableMethodFileOffsets_getContainerOffset; # systemapi + ADynamicInstrumentationManager_ExecutableMethodFileOffsets_getMethodOffset; # systemapi + ADynamicInstrumentationManager_ExecutableMethodFileOffsets_destroy; # systemapi AAssetDir_close; AAssetDir_getNextFileName; AAssetDir_rewind; diff --git a/nfc/api/system-current.txt b/nfc/api/system-current.txt index 675c8f80add2..c25f77b12116 100644 --- a/nfc/api/system-current.txt +++ b/nfc/api/system-current.txt @@ -100,12 +100,14 @@ package android.nfc { method public void onHceEventReceived(int); method public void onLaunchHceAppChooserActivity(@NonNull String, @NonNull java.util.List<android.nfc.cardemulation.ApduServiceInfo>, @NonNull android.content.ComponentName, @NonNull String); method public void onLaunchHceTapAgainDialog(@NonNull android.nfc.cardemulation.ApduServiceInfo, @NonNull String); + method public void onLogEventNotified(@NonNull android.nfc.OemLogItems); method public void onNdefMessage(@NonNull android.nfc.Tag, @NonNull android.nfc.NdefMessage, @NonNull java.util.function.Consumer<java.lang.Boolean>); method public void onNdefRead(@NonNull java.util.function.Consumer<java.lang.Boolean>); method public void onReaderOptionChanged(boolean); method public void onRfDiscoveryStarted(boolean); method public void onRfFieldActivated(boolean); method public void onRoutingChanged(); + method public void onRoutingTableFull(); method public void onStateUpdated(int); method public void onTagConnected(boolean); method public void onTagDispatch(@NonNull java.util.function.Consumer<java.lang.Boolean>); @@ -115,6 +117,27 @@ package android.nfc { method public int getNfceeId(); } + @FlaggedApi("android.nfc.nfc_oem_extension") public final class OemLogItems implements android.os.Parcelable { + method public int describeContents(); + method public int getAction(); + method public int getCallingPid(); + method @Nullable public byte[] getCommandApdu(); + method public int getEvent(); + method @Nullable public byte[] getResponseApdu(); + method @Nullable public java.time.Instant getRfFieldEventTimeMillis(); + method @Nullable public android.nfc.Tag getTag(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.nfc.OemLogItems> CREATOR; + field public static final int EVENT_DISABLE = 2; // 0x2 + field public static final int EVENT_ENABLE = 1; // 0x1 + field public static final int EVENT_UNSET = 0; // 0x0 + field public static final int LOG_ACTION_HCE_DATA = 516; // 0x204 + field public static final int LOG_ACTION_NFC_TOGGLE = 513; // 0x201 + field public static final int LOG_ACTION_RF_FIELD_STATE_CHANGED = 1; // 0x1 + field public static final int LOG_ACTION_SCREEN_STATE_CHANGED = 518; // 0x206 + field public static final int LOG_ACTION_TAG_DETECTED = 3; // 0x3 + } + @FlaggedApi("android.nfc.nfc_oem_extension") public class RoutingStatus { method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public int getDefaultIsoDepRoute(); method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public int getDefaultOffHostRoute(); diff --git a/nfc/java/android/nfc/INfcOemExtensionCallback.aidl b/nfc/java/android/nfc/INfcOemExtensionCallback.aidl index 7f1fd15fe68a..fb793b024288 100644 --- a/nfc/java/android/nfc/INfcOemExtensionCallback.aidl +++ b/nfc/java/android/nfc/INfcOemExtensionCallback.aidl @@ -18,6 +18,7 @@ package android.nfc; import android.content.ComponentName; import android.nfc.cardemulation.ApduServiceInfo; import android.nfc.NdefMessage; +import android.nfc.OemLogItems; import android.nfc.Tag; import android.os.ResultReceiver; @@ -51,4 +52,6 @@ interface INfcOemExtensionCallback { void onNdefMessage(in Tag tag, in NdefMessage message, in ResultReceiver hasOemExecutableContent); void onLaunchHceAppChooserActivity(in String selectedAid, in List<ApduServiceInfo> services, in ComponentName failedComponent, in String category); void onLaunchHceTapAgainActivity(in ApduServiceInfo service, in String category); + void onRoutingTableFull(); + void onLogEventNotified(in OemLogItems item); } diff --git a/nfc/java/android/nfc/NfcOemExtension.java b/nfc/java/android/nfc/NfcOemExtension.java index 1bfe71461ac3..57ee981caf9c 100644 --- a/nfc/java/android/nfc/NfcOemExtension.java +++ b/nfc/java/android/nfc/NfcOemExtension.java @@ -392,6 +392,19 @@ public final class NfcOemExtension { * @param category the category of the service */ void onLaunchHceTapAgainDialog(@NonNull ApduServiceInfo service, @NonNull String category); + + /** + * Callback to indicate that routing table is full and the OEM can optionally launch a + * dialog to request the user to remove some Card Emulation apps from the device to free + * routing table space. + */ + void onRoutingTableFull(); + + /** + * Callback when OEM specified log event are notified. + * @param item the log items that contains log information of NFC event. + */ + void onLogEventNotified(@NonNull OemLogItems item); } @@ -847,6 +860,12 @@ public final class NfcOemExtension { handleVoidCallback(enabled, cb::onReaderOptionChanged, ex)); } + public void onRoutingTableFull() throws RemoteException { + mCallbackMap.forEach((cb, ex) -> + handleVoidCallback(null, + (Object input) -> cb.onRoutingTableFull(), ex)); + } + @Override public void onGetOemAppSearchIntent(List<String> packages, ResultReceiver intentConsumer) throws RemoteException { @@ -900,6 +919,12 @@ public final class NfcOemExtension { handleVoid2ArgCallback(service, category, cb::onLaunchHceTapAgainDialog, ex)); } + @Override + public void onLogEventNotified(OemLogItems item) throws RemoteException { + mCallbackMap.forEach((cb, ex) -> + handleVoidCallback(item, cb::onLogEventNotified, ex)); + } + private <T> void handleVoidCallback( T input, Consumer<T> callbackMethod, Executor executor) { synchronized (mLock) { diff --git a/nfc/java/android/nfc/OemLogItems.aidl b/nfc/java/android/nfc/OemLogItems.aidl new file mode 100644 index 000000000000..3bcb445fc7d2 --- /dev/null +++ b/nfc/java/android/nfc/OemLogItems.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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; + +parcelable OemLogItems;
\ No newline at end of file diff --git a/nfc/java/android/nfc/OemLogItems.java b/nfc/java/android/nfc/OemLogItems.java new file mode 100644 index 000000000000..6671941c1cc9 --- /dev/null +++ b/nfc/java/android/nfc/OemLogItems.java @@ -0,0 +1,325 @@ +/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nfc;
+
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.time.Instant;
+
+/**
+ * A log class for OEMs to get log information of NFC events.
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
+@SystemApi
+public final class OemLogItems implements Parcelable {
+ /**
+ * Used when RF field state is changed.
+ */
+ public static final int LOG_ACTION_RF_FIELD_STATE_CHANGED = 0X01;
+ /**
+ * Used when NFC is toggled. Event should be set to {@link LogEvent#EVENT_ENABLE} or
+ * {@link LogEvent#EVENT_DISABLE} if this action is used.
+ */
+ public static final int LOG_ACTION_NFC_TOGGLE = 0x0201;
+ /**
+ * Used when sending host routing status.
+ */
+ public static final int LOG_ACTION_HCE_DATA = 0x0204;
+ /**
+ * Used when screen state is changed.
+ */
+ public static final int LOG_ACTION_SCREEN_STATE_CHANGED = 0x0206;
+ /**
+ * Used when tag is detected.
+ */
+ public static final int LOG_ACTION_TAG_DETECTED = 0x03;
+
+ /**
+ * @hide
+ */
+ @IntDef(prefix = { "LOG_ACTION_" }, value = {
+ LOG_ACTION_RF_FIELD_STATE_CHANGED,
+ LOG_ACTION_NFC_TOGGLE,
+ LOG_ACTION_HCE_DATA,
+ LOG_ACTION_SCREEN_STATE_CHANGED,
+ LOG_ACTION_TAG_DETECTED,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface LogAction {}
+
+ /**
+ * Represents the event is not set.
+ */
+ public static final int EVENT_UNSET = 0;
+ /**
+ * Represents nfc enable is called.
+ */
+ public static final int EVENT_ENABLE = 1;
+ /**
+ * Represents nfc disable is called.
+ */
+ public static final int EVENT_DISABLE = 2;
+ /** @hide */
+ @IntDef(prefix = { "EVENT_" }, value = {
+ EVENT_UNSET,
+ EVENT_ENABLE,
+ EVENT_DISABLE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface LogEvent {}
+ private int mAction;
+ private int mEvent;
+ private int mCallingPid;
+ private byte[] mCommandApdus;
+ private byte[] mResponseApdus;
+ private Instant mRfFieldOnTime;
+ private Tag mTag;
+
+ /** @hide */
+ public OemLogItems(@LogAction int action, @LogEvent int event, int callingPid,
+ byte[] commandApdus, byte[] responseApdus, Instant rfFieldOnTime,
+ Tag tag) {
+ mAction = action;
+ mEvent = event;
+ mTag = tag;
+ mCallingPid = callingPid;
+ mCommandApdus = commandApdus;
+ mResponseApdus = responseApdus;
+ mRfFieldOnTime = rfFieldOnTime;
+ }
+
+ /**
+ * Describe the kinds of special objects contained in this Parcelable
+ * instance's marshaled representation. For example, if the object will
+ * include a file descriptor in the output of {@link #writeToParcel(Parcel, int)},
+ * the return value of this method must include the
+ * {@link #CONTENTS_FILE_DESCRIPTOR} bit.
+ *
+ * @return a bitmask indicating the set of special object types marshaled
+ * by this Parcelable object instance.
+ */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Flatten this object in to a Parcel.
+ *
+ * @param dest The Parcel in which the object should be written.
+ * @param flags Additional flags about how the object should be written.
+ * May be 0 or {@link #PARCELABLE_WRITE_RETURN_VALUE}.
+ */
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mAction);
+ dest.writeInt(mEvent);
+ dest.writeInt(mCallingPid);
+ dest.writeInt(mCommandApdus.length);
+ dest.writeByteArray(mCommandApdus);
+ dest.writeInt(mResponseApdus.length);
+ dest.writeByteArray(mResponseApdus);
+ dest.writeLong(mRfFieldOnTime.getEpochSecond());
+ dest.writeInt(mRfFieldOnTime.getNano());
+ dest.writeParcelable(mTag, 0);
+ }
+
+ /** @hide */
+ public static class Builder {
+ private final OemLogItems mItem;
+
+ public Builder(@LogAction int type) {
+ mItem = new OemLogItems(type, EVENT_UNSET, 0, new byte[0], new byte[0], null, null);
+ }
+
+ /** Setter of the log action. */
+ public OemLogItems.Builder setAction(@LogAction int action) {
+ mItem.mAction = action;
+ return this;
+ }
+
+ /** Setter of the log calling event. */
+ public OemLogItems.Builder setCallingEvent(@LogEvent int event) {
+ mItem.mEvent = event;
+ return this;
+ }
+
+ /** Setter of the log calling Pid. */
+ public OemLogItems.Builder setCallingPid(int pid) {
+ mItem.mCallingPid = pid;
+ return this;
+ }
+
+ /** Setter of APDU command. */
+ public OemLogItems.Builder setApduCommand(byte[] apdus) {
+ mItem.mCommandApdus = apdus;
+ return this;
+ }
+
+ /** Setter of RF field on time. */
+ public OemLogItems.Builder setRfFieldOnTime(Instant time) {
+ mItem.mRfFieldOnTime = time;
+ return this;
+ }
+
+ /** Setter of APDU response. */
+ public OemLogItems.Builder setApduResponse(byte[] apdus) {
+ mItem.mResponseApdus = apdus;
+ return this;
+ }
+
+ /** Setter of dispatched tag. */
+ public OemLogItems.Builder setTag(Tag tag) {
+ mItem.mTag = tag;
+ return this;
+ }
+
+ /** Builds an {@link OemLogItems} instance. */
+ public OemLogItems build() {
+ return mItem;
+ }
+ }
+
+ /**
+ * Gets the action of this log.
+ * @return one of {@link LogAction}
+ */
+ @LogAction
+ public int getAction() {
+ return mAction;
+ }
+
+ /**
+ * Gets the event of this log. This will be set to {@link LogEvent#EVENT_ENABLE} or
+ * {@link LogEvent#EVENT_DISABLE} only when action is set to
+ * {@link LogAction#LOG_ACTION_NFC_TOGGLE}
+ * @return one of {@link LogEvent}
+ */
+ @LogEvent
+ public int getEvent() {
+ return mEvent;
+ }
+
+ /**
+ * Gets the calling Pid of this log. This field will be set only when action is set to
+ * {@link LogAction#LOG_ACTION_NFC_TOGGLE}
+ * @return calling Pid
+ */
+ public int getCallingPid() {
+ return mCallingPid;
+ }
+
+ /**
+ * Gets the command APDUs of this log. This field will be set only when action is set to
+ * {@link LogAction#LOG_ACTION_HCE_DATA}
+ * @return a byte array of command APDUs with the same format as
+ * {@link android.nfc.cardemulation.HostApduService#sendResponseApdu(byte[])}
+ */
+ @Nullable
+ public byte[] getCommandApdu() {
+ return mCommandApdus;
+ }
+
+ /**
+ * Gets the response APDUs of this log. This field will be set only when action is set to
+ * {@link LogAction#LOG_ACTION_HCE_DATA}
+ * @return a byte array of response APDUs with the same format as
+ * {@link android.nfc.cardemulation.HostApduService#sendResponseApdu(byte[])}
+ */
+ @Nullable
+ public byte[] getResponseApdu() {
+ return mResponseApdus;
+ }
+
+ /**
+ * Gets the RF field event time in this log in millisecond. This field will be set only when
+ * action is set to {@link LogAction#LOG_ACTION_RF_FIELD_STATE_CHANGED}
+ * @return an {@link Instant} of RF field event time.
+ */
+ @Nullable
+ public Instant getRfFieldEventTimeMillis() {
+ return mRfFieldOnTime;
+ }
+
+ /**
+ * Gets the tag of this log. This field will be set only when action is set to
+ * {@link LogAction#LOG_ACTION_TAG_DETECTED}
+ * @return a detected {@link Tag} in {@link #LOG_ACTION_TAG_DETECTED} case. Return
+ * null otherwise.
+ */
+ @Nullable
+ public Tag getTag() {
+ return mTag;
+ }
+
+ private String byteToHex(byte[] bytes) {
+ char[] HexArray = "0123456789ABCDEF".toCharArray();
+ char[] hexChars = new char[bytes.length * 2];
+ for (int j = 0; j < bytes.length; j++) {
+ int v = bytes[j] & 0xFF;
+ hexChars[j * 2] = HexArray[v >>> 4];
+ hexChars[j * 2 + 1] = HexArray[v & 0x0F];
+ }
+ return new String(hexChars);
+ }
+
+ @Override
+ public String toString() {
+ return "[mCommandApdus: "
+ + ((mCommandApdus != null) ? byteToHex(mCommandApdus) : "null")
+ + "[mResponseApdus: "
+ + ((mResponseApdus != null) ? byteToHex(mResponseApdus) : "null")
+ + ", mCallingApi= " + mEvent
+ + ", mAction= " + mAction
+ + ", mCallingPId = " + mCallingPid
+ + ", mRfFieldOnTime= " + mRfFieldOnTime;
+ }
+ private OemLogItems(Parcel in) {
+ this.mAction = in.readInt();
+ this.mEvent = in.readInt();
+ this.mCallingPid = in.readInt();
+ this.mCommandApdus = new byte[in.readInt()];
+ in.readByteArray(this.mCommandApdus);
+ this.mResponseApdus = new byte[in.readInt()];
+ in.readByteArray(this.mResponseApdus);
+ this.mRfFieldOnTime = Instant.ofEpochSecond(in.readLong(), in.readInt());
+ this.mTag = in.readParcelable(Tag.class.getClassLoader(), Tag.class);
+ }
+
+ public static final @NonNull Parcelable.Creator<OemLogItems> CREATOR =
+ new Parcelable.Creator<OemLogItems>() {
+ @Override
+ public OemLogItems createFromParcel(Parcel in) {
+ return new OemLogItems(in);
+ }
+
+ @Override
+ public OemLogItems[] newArray(int size) {
+ return new OemLogItems[size];
+ }
+ };
+
+}
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp index e1418678a9f8..b2dcb7fa4f53 100644 --- a/packages/SettingsLib/Android.bp +++ b/packages/SettingsLib/Android.bp @@ -61,7 +61,7 @@ android_library { "SettingsLibUtils", "SettingsLibZeroStatePreference", "settingslib_media_flags_lib", - "settingslib_flags_lib", + "aconfig_settingslib_flags_java_lib", ], plugins: ["androidx.room_room-compiler-plugin"], @@ -107,20 +107,6 @@ java_aconfig_library { aconfig_declarations: "settingslib_media_flags", } -aconfig_declarations { - name: "settingslib_flags", - package: "com.android.settingslib.flags", - container: "system", - srcs: [ - "aconfig/settingslib.aconfig", - ], -} - -java_aconfig_library { - name: "settingslib_flags_lib", - aconfig_declarations: "settingslib_flags", -} - soong_config_module_type { name: "avatar_picker_java_defaults", module_type: "java_defaults", diff --git a/packages/SettingsLib/AppPreference/src/com/android/settingslib/widget/AppPreference.java b/packages/SettingsLib/AppPreference/src/com/android/settingslib/widget/AppPreference.java index 3b52df7e5fbb..c3f6eb71c2e7 100644 --- a/packages/SettingsLib/AppPreference/src/com/android/settingslib/widget/AppPreference.java +++ b/packages/SettingsLib/AppPreference/src/com/android/settingslib/widget/AppPreference.java @@ -30,21 +30,28 @@ public class AppPreference extends Preference { public AppPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); - setLayoutResource(R.layout.preference_app); + init(context); } public AppPreference(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); - setLayoutResource(R.layout.preference_app); + init(context); } public AppPreference(Context context) { super(context); - setLayoutResource(R.layout.preference_app); + init(context); } public AppPreference(Context context, AttributeSet attrs) { super(context, attrs); - setLayoutResource(R.layout.preference_app); + init(context); + } + + private void init(Context context) { + int resId = SettingsThemeHelper.isExpressiveTheme(context) + ? com.android.settingslib.widget.theme.R.layout.settingslib_expressive_preference + : R.layout.preference_app; + setLayoutResource(resId); } } diff --git a/packages/SettingsLib/AppPreference/src/com/android/settingslib/widget/AppSwitchPreference.java b/packages/SettingsLib/AppPreference/src/com/android/settingslib/widget/AppSwitchPreference.java index ecd500e1a160..3dcdfbaeb8b3 100644 --- a/packages/SettingsLib/AppPreference/src/com/android/settingslib/widget/AppSwitchPreference.java +++ b/packages/SettingsLib/AppPreference/src/com/android/settingslib/widget/AppSwitchPreference.java @@ -32,22 +32,29 @@ public class AppSwitchPreference extends SwitchPreferenceCompat { public AppSwitchPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); - setLayoutResource(R.layout.preference_app); + init(context); } public AppSwitchPreference(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); - setLayoutResource(R.layout.preference_app); + init(context); } public AppSwitchPreference(Context context, AttributeSet attrs) { super(context, attrs); - setLayoutResource(R.layout.preference_app); + init(context); } public AppSwitchPreference(Context context) { super(context); - setLayoutResource(R.layout.preference_app); + init(context); + } + + private void init(Context context) { + int resId = SettingsThemeHelper.isExpressiveTheme(context) + ? com.android.settingslib.widget.theme.R.layout.settingslib_expressive_preference + : R.layout.preference_app; + setLayoutResource(resId); } @Override diff --git a/packages/SettingsLib/ButtonPreference/src/com/android/settingslib/widget/ButtonPreference.java b/packages/SettingsLib/ButtonPreference/src/com/android/settingslib/widget/ButtonPreference.java index 979ff96be3f7..993555e78bea 100644 --- a/packages/SettingsLib/ButtonPreference/src/com/android/settingslib/widget/ButtonPreference.java +++ b/packages/SettingsLib/ButtonPreference/src/com/android/settingslib/widget/ButtonPreference.java @@ -37,7 +37,7 @@ import com.google.android.material.button.MaterialButton; /** * A preference handled a button */ -public class ButtonPreference extends Preference { +public class ButtonPreference extends Preference implements GroupSectionDividerMixin { enum ButtonStyle { FILLED_NORMAL(0, 0, R.layout.settingslib_expressive_button_filled), diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt index 843d2aadf333..cd03dd7ca1b3 100644 --- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt +++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt @@ -202,6 +202,12 @@ open class KeyedDataObservable<K> : KeyedObservable<K> { entry.value.execute { observer.onKeyChanged(key, reason) } } } + + fun hasAnyObserver(): Boolean { + synchronized(observers) { if (observers.isNotEmpty()) return true } + synchronized(keyedObservers) { if (keyedObservers.isNotEmpty()) return true } + return false + } } /** [KeyedObservable] with no-op implementations for all interfaces. */ diff --git a/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java b/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java index d60290ed91ef..37f47543c536 100644 --- a/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java +++ b/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java @@ -43,7 +43,7 @@ import java.net.URISyntaxException; * A custom preference acting as "footer" of a page. It has a field for icon and text. It is added * to screen as the last preference. */ -public class FooterPreference extends Preference { +public class FooterPreference extends Preference implements GroupSectionDividerMixin { private static final String TAG = "FooterPreference"; public static final String KEY_FOOTER = "footer_preference"; diff --git a/packages/SettingsLib/IntroPreference/Android.bp b/packages/SettingsLib/IntroPreference/Android.bp index 155db186c702..8f9fb7a2e408 100644 --- a/packages/SettingsLib/IntroPreference/Android.bp +++ b/packages/SettingsLib/IntroPreference/Android.bp @@ -29,5 +29,6 @@ android_library { min_sdk_version: "21", apex_available: [ "//apex_available:platform", + "com.android.healthfitness", ], } diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceHierarchy.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceHierarchy.kt index 94d373bed0a5..a2b826a50e58 100644 --- a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceHierarchy.kt +++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceHierarchy.kt @@ -136,6 +136,18 @@ class PreferenceHierarchy internal constructor(metadata: PreferenceMetadata) : for (it in children) action(it) } + /** Traversals preference hierarchy recursively and applies given action. */ + fun forEachRecursively(action: (PreferenceHierarchyNode) -> Unit) { + action(this) + for (child in children) { + if (child is PreferenceHierarchy) { + child.forEachRecursively(action) + } else { + action(child) + } + } + } + /** Traversals preference hierarchy and applies given action. */ suspend fun forEachAsync(action: suspend (PreferenceHierarchyNode) -> Unit) { for (it in children) action(it) @@ -154,21 +166,6 @@ class PreferenceHierarchy internal constructor(metadata: PreferenceMetadata) : } return null } - - /** Returns all the [PreferenceHierarchyNode]s appear in the hierarchy. */ - fun getAllPreferences(): List<PreferenceHierarchyNode> = - mutableListOf<PreferenceHierarchyNode>().also { getAllPreferences(it) } - - private fun getAllPreferences(result: MutableList<PreferenceHierarchyNode>) { - result.add(this) - for (child in children) { - if (child is PreferenceHierarchy) { - child.getAllPreferences(result) - } else { - result.add(child) - } - } - } } /** diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBinding.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBinding.kt index 49acc1d44144..6b7be91c1903 100644 --- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBinding.kt +++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceBinding.kt @@ -108,6 +108,9 @@ interface PreferenceBinding { } } +/** Interface indicates that a virtual [Preference] should be created for binding. */ +interface PreferenceBindingPlaceholder + /** Abstract preference screen to provide preference hierarchy and binding factory. */ interface PreferenceScreenCreator : PreferenceScreenMetadata, PreferenceScreenProvider { diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceFragment.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceFragment.kt index 41a626fe8efa..991d5b7791e9 100644 --- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceFragment.kt +++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceFragment.kt @@ -32,7 +32,7 @@ import com.android.settingslib.widget.SettingsBasePreferenceFragment open class PreferenceFragment : SettingsBasePreferenceFragment(), PreferenceScreenProvider, PreferenceScreenBindingKeyProvider { - private var preferenceScreenBindingHelper: PreferenceScreenBindingHelper? = null + protected var preferenceScreenBindingHelper: PreferenceScreenBindingHelper? = null override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { preferenceScreen = createPreferenceScreen() @@ -129,7 +129,9 @@ open class PreferenceFragment : } protected fun getPreferenceKeysInHierarchy(): Set<String> = - preferenceScreenBindingHelper?.getPreferences()?.map { it.metadata.key }?.toSet() ?: setOf() + preferenceScreenBindingHelper?.let { + mutableSetOf<String>().apply { it.forEachRecursively { add(it.metadata.key) } } + } ?: setOf() companion object { private const val TAG = "PreferenceFragment" diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt index 022fb1dbe99c..cfe6089169d3 100644 --- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt +++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt @@ -143,7 +143,8 @@ class PreferenceScreenBindingHelper( } } - fun getPreferences() = preferenceHierarchy.getAllPreferences() + fun forEachRecursively(action: (PreferenceHierarchyNode) -> Unit) = + preferenceHierarchy.forEachRecursively(action) fun onCreate() { for (preference in lifecycleAwarePreferences) { @@ -191,11 +192,11 @@ class PreferenceScreenBindingHelper( companion object { /** Preference value is changed. */ - private const val CHANGE_REASON_VALUE = 0 + const val CHANGE_REASON_VALUE = 0 /** Preference state (title/summary, enable state, etc.) is changed. */ - private const val CHANGE_REASON_STATE = 1 + const val CHANGE_REASON_STATE = 1 /** Dependent preference state is changed. */ - private const val CHANGE_REASON_DEPENDENT = 2 + const val CHANGE_REASON_DEPENDENT = 2 /** Updates preference screen that has incomplete hierarchy. */ @JvmStatic @@ -217,34 +218,47 @@ class PreferenceScreenBindingHelper( preferenceScreen: PreferenceScreen, preferenceBindingFactory: PreferenceBindingFactory, preferenceHierarchy: PreferenceHierarchy, - ) = - preferenceScreen.bindRecursively( - preferenceBindingFactory, - preferenceHierarchy.getAllPreferences().associateBy { it.metadata.key }, - ) - - private fun PreferenceGroup.bindRecursively( - preferenceBindingFactory: PreferenceBindingFactory, - preferences: Map<String, PreferenceHierarchyNode>, - storages: MutableMap<KeyValueStore, PreferenceDataStore> = mutableMapOf(), ) { - preferences[key]?.let { preferenceBindingFactory.bind(this, it) } - val count = preferenceCount - for (index in 0 until count) { - val preference = getPreference(index) - if (preference is PreferenceGroup) { - preference.bindRecursively(preferenceBindingFactory, preferences, storages) - } else { - preferences[preference.key]?.let { - val metadata = it.metadata - (metadata as? PersistentPreference<*>)?.storage(context)?.let { storage -> - preference.preferenceDataStore = - storages.getOrPut(storage) { PreferenceDataStoreAdapter(storage) } + val preferences = mutableMapOf<String, PreferenceHierarchyNode>() + preferenceHierarchy.forEachRecursively { + val metadata = it.metadata + preferences[metadata.key] = it + } + val storages = mutableMapOf<KeyValueStore, PreferenceDataStore>() + + fun Preference.setPreferenceDataStore(metadata: PreferenceMetadata) { + (metadata as? PersistentPreference<*>)?.storage(context)?.let { storage -> + preferenceDataStore = + storages.getOrPut(storage) { PreferenceDataStoreAdapter(storage) } + } + } + + fun PreferenceGroup.bindRecursively() { + preferences.remove(key)?.let { preferenceBindingFactory.bind(this, it) } + val count = preferenceCount + for (index in 0 until count) { + val preference = getPreference(index) + if (preference is PreferenceGroup) { + preference.bindRecursively() + } else { + preferences.remove(preference.key)?.let { + preference.setPreferenceDataStore(it.metadata) + preferenceBindingFactory.bind(preference, it) } - preferenceBindingFactory.bind(preference, it) } } } + + preferenceScreen.bindRecursively() + for (node in preferences.values) { + val metadata = node.metadata + val binding = preferenceBindingFactory.getPreferenceBinding(metadata) + if (binding !is PreferenceBindingPlaceholder) continue + val preference = binding.createWidget(preferenceScreen.context) + preference.setPreferenceDataStore(metadata) + preferenceBindingFactory.bind(preference, node, binding) + preferenceScreen.addPreference(preference) + } } } } diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/Android.bp b/packages/SettingsLib/SelectorWithWidgetPreference/Android.bp index 155ee831ae52..78e27fe5ad04 100644 --- a/packages/SettingsLib/SelectorWithWidgetPreference/Android.bp +++ b/packages/SettingsLib/SelectorWithWidgetPreference/Android.bp @@ -29,6 +29,7 @@ android_library { "//apex_available:platform", "com.android.permission", "com.android.mediaprovider", + "com.android.healthfitness", ], } @@ -51,5 +52,6 @@ java_aconfig_library { "//apex_available:platform", "com.android.permission", "com.android.mediaprovider", + "com.android.healthfitness", ], } diff --git a/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference_icon_frame.xml b/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference_icon_frame.xml index ccdf37d452b0..0cd0b3cb14f1 100644 --- a/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference_icon_frame.xml +++ b/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference_icon_frame.xml @@ -22,7 +22,7 @@ android:minWidth="@dimen/settingslib_expressive_space_medium3" android:minHeight="@dimen/settingslib_expressive_space_medium3" android:gravity="center" - android:layout_marginEnd="-8dp" + android:layout_marginEnd="-4dp" android:filterTouchesWhenObscured="false"> <androidx.preference.internal.PreferenceImageView diff --git a/packages/SettingsLib/SettingsTheme/res/values-v35/styles_preference_expressive.xml b/packages/SettingsLib/SettingsTheme/res/values-v35/styles_preference_expressive.xml index 3c69027c2080..cec8e45e2bfb 100644 --- a/packages/SettingsLib/SettingsTheme/res/values-v35/styles_preference_expressive.xml +++ b/packages/SettingsLib/SettingsTheme/res/values-v35/styles_preference_expressive.xml @@ -36,33 +36,34 @@ <style name="SettingsLibPreference.SwitchPreference" parent="SettingsSwitchPreference.SettingsLib"/> <style name="SettingsLibPreference.Expressive"> - <item name="android:layout">@layout/settingslib_expressive_preference</item> + <item name="layout">@layout/settingslib_expressive_preference</item> </style> <style name="SettingsLibPreference.Category.Expressive"> </style> <style name="SettingsLibPreference.CheckBoxPreference.Expressive"> - <item name="android:layout">@layout/settingslib_expressive_preference</item> + <item name="layout">@layout/settingslib_expressive_preference</item> </style> <style name="SettingsLibPreference.SwitchPreferenceCompat.Expressive"> - <item name="android:layout">@layout/settingslib_expressive_preference</item> + <item name="layout">@layout/settingslib_expressive_preference</item> <item name="android:widgetLayout">@layout/settingslib_expressive_preference_switch</item> </style> <style name="SettingsLibPreference.SeekBarPreference.Expressive"/> <style name="SettingsLibPreference.PreferenceScreen.Expressive"> - <item name="android:layout">@layout/settingslib_expressive_preference</item> + <item name="layout">@layout/settingslib_expressive_preference</item> </style> <style name="SettingsLibPreference.DialogPreference.Expressive"> + <item name="layout">@layout/settingslib_expressive_preference</item> </style> <style name="SettingsLibPreference.DialogPreference.EditTextPreference.Expressive"> - <item name="android:layout">@layout/settingslib_expressive_preference</item> - <item name="android:dialogLayout">@layout/settingslib_preference_dialog_edittext</item> + <item name="layout">@layout/settingslib_expressive_preference</item> + <item name="dialogLayout">@layout/settingslib_preference_dialog_edittext</item> </style> <style name="SettingsLibPreference.DropDown.Expressive"> diff --git a/packages/SettingsLib/StatusBannerPreference/src/com/android/settingslib/widget/StatusBannerPreference.kt b/packages/SettingsLib/StatusBannerPreference/src/com/android/settingslib/widget/StatusBannerPreference.kt index c1578ef69635..1f8cfb5e432e 100644 --- a/packages/SettingsLib/StatusBannerPreference/src/com/android/settingslib/widget/StatusBannerPreference.kt +++ b/packages/SettingsLib/StatusBannerPreference/src/com/android/settingslib/widget/StatusBannerPreference.kt @@ -34,7 +34,7 @@ class StatusBannerPreference @JvmOverloads constructor( attrs: AttributeSet? = null, defStyleAttr: Int = 0, defStyleRes: Int = 0 -) : Preference(context, attrs, defStyleAttr, defStyleRes) { +) : Preference(context, attrs, defStyleAttr, defStyleRes), GroupSectionDividerMixin { enum class BannerStatus { GENERIC, diff --git a/packages/SettingsLib/TopIntroPreference/src/com/android/settingslib/widget/TopIntroPreference.kt b/packages/SettingsLib/TopIntroPreference/src/com/android/settingslib/widget/TopIntroPreference.kt index 5be56f8ebc86..9764e64b8509 100644 --- a/packages/SettingsLib/TopIntroPreference/src/com/android/settingslib/widget/TopIntroPreference.kt +++ b/packages/SettingsLib/TopIntroPreference/src/com/android/settingslib/widget/TopIntroPreference.kt @@ -31,7 +31,7 @@ open class TopIntroPreference @JvmOverloads constructor( attrs: AttributeSet? = null, defStyleAttr: Int = 0, defStyleRes: Int = 0 -) : Preference(context, attrs, defStyleAttr, defStyleRes) { +) : Preference(context, attrs, defStyleAttr, defStyleRes), GroupSectionDividerMixin { private var isCollapsable: Boolean = false private var minLines: Int = 2 diff --git a/packages/SettingsLib/aconfig/settingslib.aconfig b/packages/SettingsLib/aconfig/settingslib.aconfig index 81a2e6aeccc1..bf419cc46aeb 100644 --- a/packages/SettingsLib/aconfig/settingslib.aconfig +++ b/packages/SettingsLib/aconfig/settingslib.aconfig @@ -74,16 +74,6 @@ flag { } flag { - name: "volume_panel_broadcast_fix" - namespace: "systemui" - description: "Make the volume panel's repository listen for the new ACTION_CONSOLIDATED_NOTIFICATION_POLICY_CHANGED broadcast instead of ACTION_NOTIFICATION_POLICY_CHANGED" - bug: "347707024" - metadata { - purpose: PURPOSE_BUGFIX - } -} - -flag { name: "volume_dialog_audio_sharing_fix" namespace: "cross_device_experiences" description: "Gates whether to show separate volume bars during audio sharing" @@ -111,6 +101,14 @@ flag { } flag { + name: "write_system_preference_permission_enabled" + is_fixed_read_only: true + namespace: "android_settings" + description: "Enable WRITE_SYSTEM_PREFERENCE permission and appop" + bug: "375193223" +} + +flag { name: "asha_profile_access_profile_enabled_true" namespace: "accessibility" description: "Changes the return value of HearingAidProfile.accessProfileEnabled() to true" @@ -166,3 +164,10 @@ flag { description: "Enable the ambient volume control in device details and hearing devices dialog." bug: "357878944" } + +flag { + name: "settings_preference_write_consent_enabled" + namespace: "android_settings" + description: "Enable the user consent prompt before writing sensitive preferences via service" + bug: "378552675" +} diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml index 79c379996d8b..6f6a357a4a95 100644 --- a/packages/SettingsLib/res/values-af/strings.xml +++ b/packages/SettingsLib/res/values-af/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Eksterne toestel"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Gekoppelde toestel"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Hierdie foon"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Kan nie op hierdie toestel speel nie"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Gradeer rekening op om oor te skakel"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Kan nie aflaaie hier speel nie"</string> diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml index 5cde078af811..4d7731c5dd45 100644 --- a/packages/SettingsLib/res/values-am/strings.xml +++ b/packages/SettingsLib/res/values-am/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"የውጭ መሣሪያ"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"የተገናኘ መሣሪያ"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"ይህ ስልክ"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"በዚህ መሣሪያ ላይ ማጫወት አልተቻለም"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"ለመቀየር መለያ ያልቁ"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"ውርዶችን እዚህ ማጫወት አይቻልም"</string> diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml index f2d722ebe5ce..8b8131342b7d 100644 --- a/packages/SettingsLib/res/values-ar/strings.xml +++ b/packages/SettingsLib/res/values-ar/strings.xml @@ -237,7 +237,7 @@ <string name="choose_profile" msgid="343803890897657450">"تحديد الملف"</string> <string name="category_personal" msgid="6236798763159385225">"الملف الشخصي"</string> <string name="category_work" msgid="4014193632325996115">"ملف العمل"</string> - <string name="category_private" msgid="4244892185452788977">"الملف الخاص"</string> + <string name="category_private" msgid="4244892185452788977">"المساخة الخاصة"</string> <string name="category_clone" msgid="1554511758987195974">"استنساخ"</string> <string name="development_settings_title" msgid="140296922921597393">"خيارات المطورين"</string> <string name="development_settings_enable" msgid="4285094651288242183">"تفعيل خيارات المطورين"</string> @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"جهاز خارجي"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"جهاز متّصل"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"هذا الهاتف"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"لا يمكن تشغيل الوسائط هنا"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"يجب ترقية الحساب للتبديل"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"المحتوى المنزَّل غير متوافق"</string> diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml index 62d6f68e3611..02324de0276f 100644 --- a/packages/SettingsLib/res/values-as/strings.xml +++ b/packages/SettingsLib/res/values-as/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"বাহ্যিক ডিভাইচ"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"সংযোগ হৈ থকা ডিভাইচ"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"এই ফ’নটো"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"এই ডিভাইচটো প্লে\' কৰিব নোৱাৰি"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"সলনি কৰিবলৈ একাউণ্ট আপগ্ৰে’ড কৰক"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"ইয়াত ডাউনল’ডসমূহ প্লে’ কৰিব নোৱাৰি"</string> diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml index 459cfaaae9c4..25e85d53cdce 100644 --- a/packages/SettingsLib/res/values-az/strings.xml +++ b/packages/SettingsLib/res/values-az/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Xarici cihaz"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Qoşulmuş cihaz"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Bu telefon"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Bu cihazda oxutmaq olmur"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Keçirmək üçün hesabı güncəllə"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Burada endirmələri oxutmaq olmur"</string> diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml index f883c93fd15e..4214a4016965 100644 --- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml +++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Spoljni uređaj"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Povezani uređaj"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ovaj telefon"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Ne možete da pustite na ovom uređaju"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Nadogradite nalog radi prebacivanja"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Preuzimanja ne mogu da se puštaju ovde"</string> diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml index ca07c4def3a0..3e645b3f1ae0 100644 --- a/packages/SettingsLib/res/values-be/strings.xml +++ b/packages/SettingsLib/res/values-be/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Знешняя прылада"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Падключаная прылада"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Гэты тэлефон"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Не ўдаецца прайграць на гэтай прыладзе"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Для пераключэння перайдзіце на іншую версію ўліковага запісу"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Тут не ўдаецца прайграць спампоўкі"</string> diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml index 7661a31d4dc2..8bdd17ebd861 100644 --- a/packages/SettingsLib/res/values-bg/strings.xml +++ b/packages/SettingsLib/res/values-bg/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Външно устройство"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Свързано устройство"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Този телефон"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Възпроизвеждането не е възможно на това устройство"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Надстройте профила, за да превключите"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Изтеглянията не могат да се възпроизвеждат тук"</string> diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml index ca98e0e510a5..834eb1eef1ce 100644 --- a/packages/SettingsLib/res/values-bn/strings.xml +++ b/packages/SettingsLib/res/values-bn/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"এক্সটার্নাল ডিভাইস"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"কানেক্ট থাকা ডিভাইস"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"এই ফোনটি"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"এই ডিভাইসে চালানো যাবে না"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"পাল্টাতে অ্যাকাউন্ট আপগ্রেড করুন"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"এতে ডাউনলোড করা কন্টেন্ট প্লে করা যাবে না"</string> diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml index 72c2cbb9e73f..75fe8181489d 100644 --- a/packages/SettingsLib/res/values-bs/strings.xml +++ b/packages/SettingsLib/res/values-bs/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Vanjski uređaj"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Povezani uređaj"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ovaj telefon"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Nije moguće reproducirati na uređaju"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Nadogradite račun da promijenite"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Nije moguće reproducirati preuzimanja ovdje"</string> diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml index 43653582576d..2c41b1aee8ef 100644 --- a/packages/SettingsLib/res/values-ca/strings.xml +++ b/packages/SettingsLib/res/values-ca/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Dispositiu extern"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Dispositiu connectat"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Aquest telèfon"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"No es pot reproduir en aquest dispositiu"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Actualitza el compte per canviar"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Les baixades no es poden reproduir aquí"</string> diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml index 0a88338cf264..a4b491bfc1f0 100644 --- a/packages/SettingsLib/res/values-cs/strings.xml +++ b/packages/SettingsLib/res/values-cs/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Externí zařízení"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Připojené zařízení"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Tento telefon"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"V zařízení nelze přehrávat"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Účet je třeba upgradovat"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Stažený obsah zde nelze přehrát"</string> diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml index 983003bf1550..f7402732299b 100644 --- a/packages/SettingsLib/res/values-da/strings.xml +++ b/packages/SettingsLib/res/values-da/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Ekstern enhed"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Forbundet enhed"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Denne telefon"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Kan ikke afspilles på denne enhed"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Opgrader kontoen for at skifte"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Downloads kan ikke afspilles her"</string> diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml index 75ebb57843b9..c8d8cb55de99 100644 --- a/packages/SettingsLib/res/values-de/strings.xml +++ b/packages/SettingsLib/res/values-de/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Externes Gerät"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Verbundenes Gerät"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Dieses Smartphone"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Wiedergabe auf diesem Gerät nicht möglich"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Zum Umstellen Kontoupgrade durchführen"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Downloads können hier nicht abgespielt werden"</string> diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml index c838f689528e..c773372790b4 100644 --- a/packages/SettingsLib/res/values-el/strings.xml +++ b/packages/SettingsLib/res/values-el/strings.xml @@ -520,7 +520,7 @@ <string name="battery_info_status_charging_fast_v2" msgid="1825439848151256589">"Γρήγορη φόρτιση"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Ελέγχονται από το διαχειριστή"</string> <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Ελέγχεται από τη Ρύθμιση με περιορισμό"</string> - <string name="disabled" msgid="8017887509554714950">"Απενεργοποιημένο"</string> + <string name="disabled" msgid="8017887509554714950">"Απενεργοποιημένη"</string> <string name="external_source_trusted" msgid="1146522036773132905">"Επιτρέπεται"</string> <string name="external_source_untrusted" msgid="5037891688911672227">"Δεν επιτρέπεται"</string> <string name="install_other_apps" msgid="3232595082023199454">"Εγκατ. άγνωστων εφ."</string> @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Εξωτερική συσκευή"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Συνδεδεμένη συσκευή"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Αυτό το τηλέφ."</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Δεν είναι δυνατή η αναπαραγωγή σε αυτήν τη συσκευή"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Αναβαθμίστε τον λογαριασμό για εναλλαγή"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Δεν είναι δυνατή η αναπαραγωγή των λήψεων εδώ"</string> diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml index b4bf2055df0e..8fe14350f3d9 100644 --- a/packages/SettingsLib/res/values-en-rAU/strings.xml +++ b/packages/SettingsLib/res/values-en-rAU/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"External device"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Connected device"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"This phone"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Can\'t play on this device"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Upgrade account to switch"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Can\'t play downloads here"</string> diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml index af392b83cfb1..0d560ea7c8b9 100644 --- a/packages/SettingsLib/res/values-en-rCA/strings.xml +++ b/packages/SettingsLib/res/values-en-rCA/strings.xml @@ -591,6 +591,9 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"External Device"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Connected device"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"This phone"</string> + <string name="media_transfer_digital_line_name" msgid="312091711951124301">"S/PDIF"</string> + <string name="media_transfer_analog_line_name" msgid="1841163866716302104">"Analog"</string> + <string name="media_transfer_aux_line_name" msgid="894135835967856689">"AUX"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Cant play on this device"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Upgrade account to switch"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Cant play downloads here"</string> diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml index b4bf2055df0e..8fe14350f3d9 100644 --- a/packages/SettingsLib/res/values-en-rGB/strings.xml +++ b/packages/SettingsLib/res/values-en-rGB/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"External device"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Connected device"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"This phone"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Can\'t play on this device"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Upgrade account to switch"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Can\'t play downloads here"</string> diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml index b4bf2055df0e..8fe14350f3d9 100644 --- a/packages/SettingsLib/res/values-en-rIN/strings.xml +++ b/packages/SettingsLib/res/values-en-rIN/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"External device"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Connected device"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"This phone"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Can\'t play on this device"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Upgrade account to switch"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Can\'t play downloads here"</string> diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml index 3c1a27822a3c..99dd4d9a56b9 100644 --- a/packages/SettingsLib/res/values-es-rUS/strings.xml +++ b/packages/SettingsLib/res/values-es-rUS/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Dispositivo externo"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Dispositivo conectado"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Este teléfono"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"No se puede reproducir en este dispositivo"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Actualiza la cuenta para cambiar"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"No se pueden reproducir las descargas aquí"</string> diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml index bafa654a5284..cbbadd44d357 100644 --- a/packages/SettingsLib/res/values-es/strings.xml +++ b/packages/SettingsLib/res/values-es/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Dispositivo externo"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Dispositivo conectado"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Este teléfono"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"No se puede reproducir en este dispositivo"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Actualiza la cuenta para cambiar"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"No se pueden reproducir descargas aquí"</string> diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml index 5f5a38c1464b..3c337933fbfb 100644 --- a/packages/SettingsLib/res/values-et/strings.xml +++ b/packages/SettingsLib/res/values-et/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Väline seade"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Ühendatud seade"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"See telefon"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Selles seadmes ei saa esitada"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Lülitamiseks täiendage kontot"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Siin ei saa allalaaditud faile esitada"</string> diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml index 48e4f62e5865..c9947485e5f2 100644 --- a/packages/SettingsLib/res/values-eu/strings.xml +++ b/packages/SettingsLib/res/values-eu/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Kanpoko gailua"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Konektatutako gailua"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Telefono hau"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Ezin da erreproduzitu gailu honetan"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Aldatzeko, bertsio-berritu kontua"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Deskargak ezin dira hemen erreproduzitu"</string> diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml index 1aeca86aed6c..75b805109824 100644 --- a/packages/SettingsLib/res/values-fa/strings.xml +++ b/packages/SettingsLib/res/values-fa/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"دستگاه خارجی"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"دستگاه متصل"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"این تلفن"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"نمیتوان در این دستگاه پخش کرد"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"برای تغییر، حساب را ارتقا دهید"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"نمیتوان بارگیریها را در اینجا پخش کرد"</string> diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml index 53724a3b7d86..9cc33b013979 100644 --- a/packages/SettingsLib/res/values-fi/strings.xml +++ b/packages/SettingsLib/res/values-fi/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Ulkoinen laite"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Yhdistetty laite"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Tämä puhelin"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Ei voi toistaa tällä laitteella"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Vaihda päivittämällä tili"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Latauksia ei voi toistaa täällä"</string> diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml index 1e4bbc43ed08..28692d0afa55 100644 --- a/packages/SettingsLib/res/values-fr-rCA/strings.xml +++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml @@ -512,7 +512,7 @@ <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"En recharge sans fil"</string> <string name="battery_info_status_charging_dock" msgid="8573274094093364791">"Recharge en cours…"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"N\'est pas en charge"</string> - <string name="battery_info_status_not_charging" msgid="1103084691314264664">"Connecté, mais ne se recharge pas"</string> + <string name="battery_info_status_not_charging" msgid="1103084691314264664">"Appareil connecté, mais pas en cours de recharge"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Chargée"</string> <string name="battery_info_status_full_charged" msgid="3536054261505567948">"Complètement rechargée"</string> <string name="battery_info_status_charging_on_hold" msgid="6364355145521694438">"Recharge en pause"</string> @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Appareil externe"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Appareil connecté"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ce téléphone"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Impossible de faire jouer le contenu sur cet appareil"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Mettez à jour le compte pour passer à la version payante"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Lecture des téléchargements impossible ici"</string> diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml index f4afc8701609..4e7abc938ccf 100644 --- a/packages/SettingsLib/res/values-fr/strings.xml +++ b/packages/SettingsLib/res/values-fr/strings.xml @@ -464,7 +464,7 @@ <string name="transcode_disable_cache" msgid="3160069309377467045">"Désactiver la cache de transcodage"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Services en cours d\'exécution"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Afficher et contrôler les services en cours d\'exécution"</string> - <string name="select_webview_provider_title" msgid="3917815648099445503">"Mise en œuvre WebView"</string> + <string name="select_webview_provider_title" msgid="3917815648099445503">"Implémentation WebView"</string> <string name="select_webview_provider_dialog_title" msgid="2444261109877277714">"Définir la mise en œuvre WebView"</string> <string name="select_webview_provider_toast_text" msgid="8512254949169359848">"Ce choix n\'est plus valide. Réessayez."</string> <string name="picture_color_mode" msgid="1013807330552931903">"Mode de couleur des images"</string> @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Appareil externe"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Appareil connecté"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ce téléphone"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Impossible de lire du contenu sur cet appareil"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Mettez à niveau le compte pour changer"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Impossible de lire les téléchargements ici"</string> diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml index be1fac816f7d..a4d3a0a4f751 100644 --- a/packages/SettingsLib/res/values-gl/strings.xml +++ b/packages/SettingsLib/res/values-gl/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Dispositivo externo"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Dispositivo conectado"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Este teléfono"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Non se pode reproducir contido neste dispositivo"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Cambia a conta a un plan superior para facer a modificación"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Non se poden reproducir as descargas neste dispositivo"</string> diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml index 05ec0e15da65..7a26463bc9bd 100644 --- a/packages/SettingsLib/res/values-gu/strings.xml +++ b/packages/SettingsLib/res/values-gu/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"બહારનું ડિવાઇસ"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"કનેક્ટ કરેલું ડિવાઇસ"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"આ ફોન"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"આ ડિવાઇસ પર ચલાવી શકતા નથી"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"સ્વિચ કરવા માટે એકાઉન્ટ અપગ્રેડ કરો"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"ડાઉનલોડ કરેલું કન્ટેન્ટ અહીં ચલાવી શકતા નથી"</string> diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml index 352dba79565d..83cb3989e909 100644 --- a/packages/SettingsLib/res/values-hi/strings.xml +++ b/packages/SettingsLib/res/values-hi/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"बाहरी डिवाइस"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"कनेक्ट किया गया डिवाइस"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"यह फ़ोन"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"इस डिवाइस पर मीडिया नहीं चलाया जा सकता"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"प्रीमियम खाते में स्विच करने के लिए, अपना खाता अपग्रेड करें"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"डाउनलोड किए गए वीडियो यहां नहीं चलाए जा सकते"</string> diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml index 2cf02fc2ff39..e78884d65538 100644 --- a/packages/SettingsLib/res/values-hr/strings.xml +++ b/packages/SettingsLib/res/values-hr/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Vanjski uređaj"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Povezani uređaj"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ovaj telefon"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Ne može se reproducirati ovdje"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Nadogradite i prebacite se"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Ne može se tu reproducirati"</string> diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml index 86a959a97cd0..ca18ac2e191f 100644 --- a/packages/SettingsLib/res/values-hu/strings.xml +++ b/packages/SettingsLib/res/values-hu/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Külső eszköz"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Csatlakoztatott eszköz"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ez a telefon"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Nem játszható le ezen az eszközön"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"A váltáshoz frissítse fiókját"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Itt nem játszhatók le a letöltések"</string> diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml index 4c29740b552a..0749d6042b6b 100644 --- a/packages/SettingsLib/res/values-hy/strings.xml +++ b/packages/SettingsLib/res/values-hy/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Արտաքին սարք"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Միացված սարք"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Այս հեռախոսը"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Հնարավոր չէ նվագարկել այս սարքում"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Փոխելու համար անցեք հաշվի պրեմիում տարբերակին"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Ներբեռնումները չեն նվագարկվում այստեղ"</string> diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml index bfd8f397be60..7d652c9db683 100644 --- a/packages/SettingsLib/res/values-in/strings.xml +++ b/packages/SettingsLib/res/values-in/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Perangkat Eksternal"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Perangkat yang terhubung"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ponsel ini"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Tidak dapat memutar di perangkat ini"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Upgrade akun untuk beralih"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Tidak dapat memutar hasil download di sini"</string> diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml index d0184e112fc0..67f4fe1b9562 100644 --- a/packages/SettingsLib/res/values-is/strings.xml +++ b/packages/SettingsLib/res/values-is/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Ytra tæki"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Tengt tæki"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Þessi sími"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Ekki er hægt að spila í þessu tæki"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Uppfærðu reikninginn til að skipta"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Ekki er hægt að spila niðurhal hér"</string> diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml index 773aa2659171..9f7f41b8b16f 100644 --- a/packages/SettingsLib/res/values-it/strings.xml +++ b/packages/SettingsLib/res/values-it/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Dispositivo esterno"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Dispositivo connesso"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Questo smartphone"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Impossibile riprodurre su questo dispositivo"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Esegui l\'upgrade dell\'account per cambiare"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Qui non è possibile riprodurre i download"</string> diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml index 5f63ed371f42..056aae170610 100644 --- a/packages/SettingsLib/res/values-iw/strings.xml +++ b/packages/SettingsLib/res/values-iw/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"מכשיר חיצוני"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"המכשיר המחובר"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"הטלפון הזה"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"לא ניתן להפעיל במכשיר"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"יש לשדרג חשבון כדי לעבור"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"לא ניתן להפעיל הורדות"</string> diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml index ae3f1970d206..833d1dcf4aef 100644 --- a/packages/SettingsLib/res/values-ja/strings.xml +++ b/packages/SettingsLib/res/values-ja/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"外部デバイス"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"接続済みのデバイス"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"このデバイス"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"このデバイスでは再生できません"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"アカウントを更新して切り替えてください"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"再生不可: ダウンロードしたコンテンツ"</string> diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml index fe9dbb52cdd0..acad174698fb 100644 --- a/packages/SettingsLib/res/values-ka/strings.xml +++ b/packages/SettingsLib/res/values-ka/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"გარე მოწყობილობა"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"დაკავშირებული მოწყობილობა"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"ეს ტელეფონი"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"ამ მოწყობილობაზე დაკვრა შეუძლებელია"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"გადასართავად განაახლეთ ანგარიში"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"შეუძლებელია აქ ჩამოტვირ. თამაში"</string> diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml index 1162e8f8a00f..e2ce5b0e8ef3 100644 --- a/packages/SettingsLib/res/values-kk/strings.xml +++ b/packages/SettingsLib/res/values-kk/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Сыртқы құрылғы"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Жалғанған құрылғы"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Осы телефон"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Осы құрылғыда ойнату мүмкін емес."</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Ауысу үшін аккаунтты жаңартыңыз."</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Жүктеп алынғандарды осы жерде ойнату мүмкін емес."</string> diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml index e6935d0d034e..c06aaeeb4bda 100644 --- a/packages/SettingsLib/res/values-km/strings.xml +++ b/packages/SettingsLib/res/values-km/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"ឧបករណ៍ខាងក្រៅ"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"ឧបករណ៍ដែលបានភ្ជាប់"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"ទូរសព្ទនេះ"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"មិនអាចចាក់នៅលើឧបករណ៍នេះបានទេ"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"ដំឡើងកម្រិតគណនី ដើម្បីប្ដូរ"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"មិនអាចចាក់ខ្លឹមសារដែលបានទាញយកនៅទីនេះបានទេ"</string> diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml index f34a43cfdd19..2c25501e60a7 100644 --- a/packages/SettingsLib/res/values-kn/strings.xml +++ b/packages/SettingsLib/res/values-kn/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"ಬಾಹ್ಯ ಸಾಧನ"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"ಕನೆಕ್ಟ್ ಮಾಡಿರುವ ಸಾಧನ"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"ಈ ಫೋನ್"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"ಈ ಸಾಧನದಲ್ಲಿ ಪ್ಲೇ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"ಬದಲಾಯಿಸಲು ಖಾತೆಯನ್ನು ಅಪ್ಗ್ರೇಡ್ ಮಾಡಿ"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"ಇಲ್ಲಿ ಡೌನ್ಲೋಡ್ಗಳನ್ನು ಪ್ಲೇ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ"</string> diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml index 1e51ae640301..f7dc6ae8603d 100644 --- a/packages/SettingsLib/res/values-ko/strings.xml +++ b/packages/SettingsLib/res/values-ko/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"외부 기기"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"연결된 기기"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"이 휴대전화"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"이 기기에서 재생할 수 없음"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"계정을 업그레이드하여 전환하기"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"여기서 다운로드한 콘텐츠를 재생할 수 없습니다."</string> diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml index 785bf43bb4d1..6105db0bf071 100644 --- a/packages/SettingsLib/res/values-ky/strings.xml +++ b/packages/SettingsLib/res/values-ky/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Тышкы түзмөк"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Туташкан түзмөк"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ушул телефон"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Бул түзмөктө ойнотууга болбойт"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Премиум аккаунтка которулуу керек"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Жүктөлүп алынгандар ойнотулбайт"</string> diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml index 740586e91f56..df3689e72249 100644 --- a/packages/SettingsLib/res/values-lo/strings.xml +++ b/packages/SettingsLib/res/values-lo/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"ອຸປະກອນພາຍນອກ"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"ອຸປະກອນທີ່ເຊື່ອມຕໍ່"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"ໂທລະສັບນີ້"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"ຫຼິ້ນຢູ່ອຸປະກອນນີ້ບໍ່ໄດ້"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"ອັບເກຣດບັນຊີເພື່ອສະຫຼັບ"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"ບໍ່ສາມາດຫຼິ້ນເນື້ອຫາທີ່ດາວໂຫຼດຢູ່ນີ້ໄດ້"</string> diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml index 0c235b8a6f35..4d9858733f86 100644 --- a/packages/SettingsLib/res/values-lt/strings.xml +++ b/packages/SettingsLib/res/values-lt/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Išorinis įrenginys"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Prijungtas įrenginys"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Šis telefonas"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Negalima leisti šiame įrenginyje"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Jei norite perjungti, naujovinkite paskyrą"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Čia negalima paleisti atsisiuntimų"</string> diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml index daaebc880ec9..582982673441 100644 --- a/packages/SettingsLib/res/values-lv/strings.xml +++ b/packages/SettingsLib/res/values-lv/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Ārēja ierīce"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Pievienotā ierīce"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Šis tālrunis"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Nevar atskaņot šajā ierīcē."</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Lai pārslēgtu, jauniniet kontu"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Šeit nevar atskaņot lejupielādes"</string> diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml index b9d970d8388f..5d5d480ecd68 100644 --- a/packages/SettingsLib/res/values-mk/strings.xml +++ b/packages/SettingsLib/res/values-mk/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Надворешен уред"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Поврзан уред"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Овој телефон"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Не може да се пушти на уредов"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Надградете ја сметката за да се префрлите"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Не може да се пуштаат преземања тука"</string> diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml index 7dd2073642f3..71406232cb64 100644 --- a/packages/SettingsLib/res/values-ml/strings.xml +++ b/packages/SettingsLib/res/values-ml/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"ബാഹ്യ ഉപകരണം"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"കണക്റ്റ് ചെയ്ത ഉപകരണം"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"ഈ ഫോൺ"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"ഈ ഉപകരണത്തിൽ പ്ലേ ചെയ്യാൻ കഴിയില്ല"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"അക്കൗണ്ട് മാറാൻ അപ്ഗ്രേഡ് ചെയ്യുക"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"ഡൗൺലോഡുകൾ പ്ലേ ചെയ്യാനാകില്ല"</string> diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml index 4f247ab410a5..c9e0178ce54f 100644 --- a/packages/SettingsLib/res/values-mn/strings.xml +++ b/packages/SettingsLib/res/values-mn/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Гадаад төхөөрөмж"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Холбогдсон төхөөрөмж"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Энэ утас"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Энэ төхөөрөмжид тоглуулах боломжгүй"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Сэлгэхийн тулд бүртгэлийг сайжруулна уу"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Татаж авсан файлыг энд тоглуулах боломжгүй"</string> diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml index be9464c30241..a6596cdf7d0c 100644 --- a/packages/SettingsLib/res/values-mr/strings.xml +++ b/packages/SettingsLib/res/values-mr/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"बाह्य डिव्हाइस"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"कनेक्ट केलेले डिव्हाइस"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"हा फोन"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"या डिव्हाइसवर प्ले करू शकत नाही"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"स्विच करण्यासाठी खाते अपग्रेड करा"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"येथे डाउनलोड प्ले केले जाऊ शकत नाहीत"</string> diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml index 8f67d339fba0..ebde3319a806 100644 --- a/packages/SettingsLib/res/values-ms/strings.xml +++ b/packages/SettingsLib/res/values-ms/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Peranti Luar"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Peranti yang disambungkan"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Telefon ini"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Tidak dapat dimainkan pada peranti ini"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Tingkatkan akaun untuk beralih"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Tidak dapat memainkan muat turun di sini"</string> diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml index 2ced73bba26a..1131007fe1bd 100644 --- a/packages/SettingsLib/res/values-my/strings.xml +++ b/packages/SettingsLib/res/values-my/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"ပြင်ပစက်"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"ချိတ်ဆက်ကိရိယာ"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"ဤဖုန်း"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"ဤစက်ပစ္စည်းတွင် ဖွင့်၍မရပါ"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"ပြောင်းရန် အကောင့်အဆင့်ကိုမြှင့်ပါ"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"ဤနေရာတွင် ဒေါင်းလုဒ်များ ဖွင့်မရပါ"</string> diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml index e16fea25271e..1a71ec07457e 100644 --- a/packages/SettingsLib/res/values-nb/strings.xml +++ b/packages/SettingsLib/res/values-nb/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Ekstern enhet"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Tilkoblet enhet"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Denne telefonen"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Kan ikke spille på denne enheten"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Oppgrader kontoen for å bytte"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Kan ikke spille av nedlastinger her"</string> diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml index b26376179c62..dea9fcce87cf 100644 --- a/packages/SettingsLib/res/values-ne/strings.xml +++ b/packages/SettingsLib/res/values-ne/strings.xml @@ -184,7 +184,7 @@ <string name="process_kernel_label" msgid="950292573930336765">"Android OS"</string> <string name="data_usage_uninstalled_apps" msgid="1933665711856171491">"हटाइएका एपहरू"</string> <string name="data_usage_uninstalled_apps_users" msgid="5533981546921913295">"एपहरू र प्रयोगकर्ताहरू हटाइयो।"</string> - <string name="data_usage_ota" msgid="7984667793701597001">"प्रणालीसम्बन्धी अद्यावधिकहरू"</string> + <string name="data_usage_ota" msgid="7984667793701597001">"प्रणालीसम्बन्धी अपडेटहरू"</string> <string name="tether_settings_title_usb" msgid="3728686573430917722">"USB टेदरिङ"</string> <string name="tether_settings_title_wifi" msgid="4803402057533895526">"पोर्टेबल हटस्पट"</string> <string name="tether_settings_title_bluetooth" msgid="916519902721399656">"ब्लुटुथ टेदर गर्दै"</string> @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"बाह्य डिभाइस"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"कनेक्ट गरिएको डिभाइस"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"यो फोन"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"यो डिभाइसमा मिडिया प्ले गर्न मिल्दैन"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"आफूले प्रयोग गर्न चाहेको खाता अपग्रेड गर्नुहोस्"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"डाउनलोड गरिएका सामग्री यसमा प्ले गर्न मिल्दैन"</string> diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml index 75d561762967..e3be50736a88 100644 --- a/packages/SettingsLib/res/values-nl/strings.xml +++ b/packages/SettingsLib/res/values-nl/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Extern apparaat"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Verbonden apparaat"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Deze telefoon"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Kan niet afspelen op dit apparaat"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Upgrade het account om te schakelen"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Kan hier geen downloads afspelen"</string> diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml index ae1df210bce3..ea6fdba27506 100644 --- a/packages/SettingsLib/res/values-or/strings.xml +++ b/packages/SettingsLib/res/values-or/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"ଏକ୍ସଟର୍ନଲ ଡିଭାଇସ"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"କନେକ୍ଟ କରାଯାଇଥିବା ଡିଭାଇସ"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"ଏହି ଫୋନ୍"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"ଏହି ଡିଭାଇସରେ ପ୍ଲେ କରାଯାଇପାରିବ ନାହିଁ"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"ସ୍ୱିଚ କରିବା ପାଇଁ ଆକାଉଣ୍ଟକୁ ଅପଗ୍ରେଡ କରନ୍ତୁ"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"ଏଠାରେ ଡାଉନଲୋଡଗୁଡ଼ିକୁ ପ୍ଲେ କରାଯାଇପାରିବ ନାହିଁ"</string> diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml index c1ef613f046e..853405167e01 100644 --- a/packages/SettingsLib/res/values-pa/strings.xml +++ b/packages/SettingsLib/res/values-pa/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"ਬਾਹਰੀ ਡੀਵਾਈਸ"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"ਕਨੈਕਟ ਕੀਤਾ ਡੀਵਾਈਸ"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"ਇਹ ਫ਼ੋਨ"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"ਇਸ ਡੀਵਾਈਸ \'ਤੇ ਨਹੀਂ ਚਲਾਇਆ ਜਾ ਸਕਦਾ"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"ਸਵਿੱਚ ਕਰਨ ਲਈ ਖਾਤੇ ਨੂੰ ਅੱਪਗ੍ਰੇਡ ਕਰੋ"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"ਡਾਊਨਲੋਡਾਂ ਨੂੰ ਇੱਥੇ ਨਹੀਂ ਚਲਾਇਆ ਜਾ ਸਕਦਾ"</string> diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml index 0a8ea16b0279..a9d4dcbf577a 100644 --- a/packages/SettingsLib/res/values-pl/strings.xml +++ b/packages/SettingsLib/res/values-pl/strings.xml @@ -520,7 +520,7 @@ <string name="battery_info_status_charging_fast_v2" msgid="1825439848151256589">"Szybkie ładowanie"</string> <string name="disabled_by_admin_summary_text" msgid="5343911767402923057">"Kontrolowane przez administratora"</string> <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Obowiązują ustawienia z ograniczonym dostępem"</string> - <string name="disabled" msgid="8017887509554714950">"Wyłączone"</string> + <string name="disabled" msgid="8017887509554714950">"Wyłączona"</string> <string name="external_source_trusted" msgid="1146522036773132905">"Dozwolone"</string> <string name="external_source_untrusted" msgid="5037891688911672227">"Niedozwolone"</string> <string name="install_other_apps" msgid="3232595082023199454">"Instalowanie nieznanych aplikacji"</string> @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Urządzenie zewnętrzne"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Połączone urządzenie"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ten telefon"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Nie można odtworzyć na tym urządzeniu"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Aby przełączyć, potrzebujesz konta premium"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Tutaj nie można odtworzyć pobranych plików"</string> diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml index c39648817af9..eac646ff4c10 100644 --- a/packages/SettingsLib/res/values-pt-rBR/strings.xml +++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Dispositivo externo"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Dispositivo conectado"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Neste telefone"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Não é possível reproduzir neste dispositivo"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Faça upgrade da conta para trocar"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Não é possível abrir os downloads aqui"</string> diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml index be91222c38f2..d8d172d25192 100644 --- a/packages/SettingsLib/res/values-pt-rPT/strings.xml +++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Dispositivo externo"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Dispositivo associado"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Este telemóvel"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Não é possível reproduzir neste dispositivo"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Atualize a conta para mudar"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Não é possível reproduzir as transferências aqui"</string> diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml index c39648817af9..eac646ff4c10 100644 --- a/packages/SettingsLib/res/values-pt/strings.xml +++ b/packages/SettingsLib/res/values-pt/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Dispositivo externo"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Dispositivo conectado"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Neste telefone"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Não é possível reproduzir neste dispositivo"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Faça upgrade da conta para trocar"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Não é possível abrir os downloads aqui"</string> diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml index 4a0023fd6387..d507b8526c8d 100644 --- a/packages/SettingsLib/res/values-ro/strings.xml +++ b/packages/SettingsLib/res/values-ro/strings.xml @@ -196,7 +196,7 @@ <string name="launch_defaults_some" msgid="3631650616557252926">"Unele valori prestabilite sunt configurate"</string> <string name="launch_defaults_none" msgid="8049374306261262709">"Nu este configurată nicio valoare prestabilită"</string> <string name="tts_settings" msgid="8130616705989351312">"Setări redare vocală a textului"</string> - <string name="tts_settings_title" msgid="7602210956640483039">"Setări pentru redarea vocală a textului"</string> + <string name="tts_settings_title" msgid="7602210956640483039">"Redare vocală a textului"</string> <string name="tts_default_rate_title" msgid="3964187817364304022">"Ritmul vorbirii"</string> <string name="tts_default_rate_summary" msgid="3781937042151716987">"Viteza cu care este vorbit textul"</string> <string name="tts_default_pitch_title" msgid="6988592215554485479">"Înălțime"</string> @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Dispozitiv extern"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Dispozitiv conectat"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Acest telefon"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Nu se poate reda pe acest dispozitiv"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Fă upgrade contului pentru a comuta"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Aici nu se pot reda descărcări"</string> diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml index 91648df5f96f..8d07c57fec56 100644 --- a/packages/SettingsLib/res/values-ru/strings.xml +++ b/packages/SettingsLib/res/values-ru/strings.xml @@ -283,7 +283,7 @@ <string name="keep_screen_on_summary" msgid="1510731514101925829">"Во время зарядки экран будет всегда включен"</string> <string name="bt_hci_snoop_log" msgid="7291287955649081448">"Включить snoop-логи Bluetooth HCI"</string> <string name="bt_hci_snoop_log_summary" msgid="6808538971394092284">"Сохранять все пакеты Bluetooth (перезапустите Bluetooth после изменения этой настройки)"</string> - <string name="oem_unlock_enable" msgid="5334869171871566731">"Заводская разблокировка"</string> + <string name="oem_unlock_enable" msgid="5334869171871566731">"Разблокировка загрузчика"</string> <string name="oem_unlock_enable_summary" msgid="5857388174390953829">"Разрешить разблокировку загрузчика ОС"</string> <string name="confirm_enable_oem_unlock_title" msgid="8249318129774367535">"Разрешить заводскую разблокировку?"</string> <string name="confirm_enable_oem_unlock_text" msgid="854131050791011970">"ВНИМАНИЕ! Функции защиты не будут работать на устройстве, пока включен этот параметр."</string> @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Внешнее устройство"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Подключенное устройство"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Этот смартфон"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Невозможно воспроизвести на этом устройстве."</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Для переключения требуется премиум-аккаунт"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Не удается воспроизвести скачанные файлы"</string> diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml index a9004d0ebedc..6694bc552e11 100644 --- a/packages/SettingsLib/res/values-si/strings.xml +++ b/packages/SettingsLib/res/values-si/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"බාහිර උපාංගය"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"සම්බන්ධ කළ උපාංගය"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"මෙම දුරකථනය"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"මෙම උපාංගය මත ධාවනය කළ නොහැක"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"මාරු වීමට ගිණුම උත්ශ්රේණි කරන්න"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"මෙහි බාගැනීම් වාදනය කළ නොහැක"</string> diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml index 8fc99cfd7416..fa525a20ca66 100644 --- a/packages/SettingsLib/res/values-sk/strings.xml +++ b/packages/SettingsLib/res/values-sk/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Externé zariadenie"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Pripojené zariadenie"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Tento telefón"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"V tomto zariadení sa nedá prehrávať obsah"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Inovujte účet a prejdite naň"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Tu sa nedajú prehrať stiahnuté súbory"</string> diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml index 0945cc042dc5..9b426aa27e19 100644 --- a/packages/SettingsLib/res/values-sl/strings.xml +++ b/packages/SettingsLib/res/values-sl/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Zunanja naprava"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Povezana naprava"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ta telefon"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Ni mogoče predvajati v tej napravi."</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Za preklop je potrebna nadgradnja računa"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Prenosov tu ni mogoče predvajati"</string> diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml index 0b3955106557..73cc518dac0a 100644 --- a/packages/SettingsLib/res/values-sq/strings.xml +++ b/packages/SettingsLib/res/values-sq/strings.xml @@ -304,12 +304,12 @@ <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Zgjidh versionin AVRCP të Bluetooth-it"</string> <string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versioni MAP i Bluetooth-it"</string> <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"Zgjidh versionin MAP të Bluetooth-it"</string> - <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Kodeku Bluetooth Audio"</string> + <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Kodeku i audios me Bluetooth"</string> <string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"Aktivizo kodekun e audios me Bluetooth\nZgjedhja"</string> <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"Shpejtësia e shembullit të Bluetooth Audio"</string> <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="5876305103137067798">"Aktivizo kodekun e audios me Bluetooth\nZgjedhja: Shpejtësia e shembullit"</string> <string name="bluetooth_select_a2dp_codec_type_help_info" msgid="8647200416514412338">"Çaktivizimi do të thotë se nuk mbështetet nga telefoni ose kufjet"</string> - <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="6253965294594390806">"Bite për shembull Bluetooth Audio"</string> + <string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="6253965294594390806">"Bite të audios me Bluetooth për shembull"</string> <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="4898693684282596143">"Aktivizo kodekun e audios me Bluetooth\nZgjedhja: Bite për shembull"</string> <string name="bluetooth_select_a2dp_codec_channel_mode" msgid="364277285688014427">"Modaliteti i kanalit të audios me Bluetooth"</string> <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title" msgid="2076949781460359589">"Aktivizo kodekun e audios me Bluetooth\nZgjedhja: Modaliteti i kanalit"</string> @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Pajisja e jashtme"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Pajisja e lidhur"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ky telefon"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Nuk mund të luhet në këtë pajisje"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Përmirëso llogarinë për të ndryshuar"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Shkarkimet nuk mund të luhen këtu"</string> diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml index d351a12bac21..d8003d75bd4e 100644 --- a/packages/SettingsLib/res/values-sr/strings.xml +++ b/packages/SettingsLib/res/values-sr/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Спољни уређај"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Повезани уређај"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Овај телефон"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Не можете да пустите на овом уређају"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Надоградите налог ради пребацивања"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Преузимања не могу да се пуштају овде"</string> diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml index 1d7f62ac6c93..f9bd295253a6 100644 --- a/packages/SettingsLib/res/values-sv/strings.xml +++ b/packages/SettingsLib/res/values-sv/strings.xml @@ -235,7 +235,7 @@ <item msgid="6946761421234586000">"400 %"</item> </string-array> <string name="choose_profile" msgid="343803890897657450">"Välj profil"</string> - <string name="category_personal" msgid="6236798763159385225">"Privat"</string> + <string name="category_personal" msgid="6236798763159385225">"Personlig"</string> <string name="category_work" msgid="4014193632325996115">"Jobb"</string> <string name="category_private" msgid="4244892185452788977">"Privat"</string> <string name="category_clone" msgid="1554511758987195974">"Klon"</string> @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Extern enhet"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Ansluten enhet"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Den här telefonen"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Kan inte spelas på denna enhet"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Uppgradera kontot för att byta"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Det går inte att spela upp nedladdningar här"</string> diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml index 599113ce6a07..b86be31ec352 100644 --- a/packages/SettingsLib/res/values-sw/strings.xml +++ b/packages/SettingsLib/res/values-sw/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Kifaa cha Nje"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Kifaa kilichounganishwa"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Simu hii"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Huwezi kucheza maudhui kwenye kifaa hiki"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Pata toleo jipya la akaunti ili ubadilishe"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Imeshindwa kucheza maudhui yaliyopakuliwa hapa"</string> diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml index 98412c1f41f4..1fd78d3842eb 100644 --- a/packages/SettingsLib/res/values-ta/strings.xml +++ b/packages/SettingsLib/res/values-ta/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"வெளிப்புறச் சாதனம்"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"இணைக்கப்பட்டுள்ள சாதனம்"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"இந்த மொபைல்"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"இந்தச் சாதனத்தில் பிளே செய்ய முடியவில்லை"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"மாற்ற, கணக்கை மேம்படுத்துங்கள்"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"பதிவிறக்கங்களை இங்கே பிளே செய்ய முடியாது"</string> diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml index 3770bb776627..fc45d2c8e149 100644 --- a/packages/SettingsLib/res/values-te/strings.xml +++ b/packages/SettingsLib/res/values-te/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"ఎక్స్టర్నల్ పరికరం"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"కనెక్ట్ చేసిన పరికరం"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"ఈ ఫోన్"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"ఈ పరికరంలో ప్లే చేయడం సాధ్యపడదు"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"మారడానికి ఖాతాను అప్గ్రేడ్ చేయండి"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"ఇక్కడ డౌన్లోడ్లను ప్లే చేయడం సాధ్యపడదు"</string> diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml index 060b7b267624..d525bc538ef8 100644 --- a/packages/SettingsLib/res/values-th/strings.xml +++ b/packages/SettingsLib/res/values-th/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"อุปกรณ์ภายนอก"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"อุปกรณ์ที่เชื่อมต่อ"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"โทรศัพท์เครื่องนี้"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"เล่นในอุปกรณ์นี้ไม่ได้"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"อัปเกรดบัญชีเพื่อเปลี่ยน"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"เล่นเนื้อหาที่ดาวน์โหลดที่นี่ไม่ได้"</string> diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml index e869e7f3bbb4..1df74736d879 100644 --- a/packages/SettingsLib/res/values-tl/strings.xml +++ b/packages/SettingsLib/res/values-tl/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"External na Device"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Nakakonektang device"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ang teleponong ito"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Hindi ma-play sa device na ito"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"I-upgrade ang account para lumipat"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Hindi mape-play ang mga download dito"</string> diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml index 188f5aa2ce8e..d62269605e84 100644 --- a/packages/SettingsLib/res/values-tr/strings.xml +++ b/packages/SettingsLib/res/values-tr/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Harici Cihaz"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Bağlı cihaz"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Bu telefon"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Bu cihazda oynatılamıyor"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Geçiş yapmak için hesabı yükseltin"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"İndirilenler burada oynatılamaz"</string> diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml index b82db3a8a85f..eb10cb209c20 100644 --- a/packages/SettingsLib/res/values-uk/strings.xml +++ b/packages/SettingsLib/res/values-uk/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Зовнішній пристрій"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Підключений пристрій"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Цей телефон"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Не можна відтворювати тут"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Потрібний платний обліковий запис"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Завантаження не відтворюватимуться"</string> diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml index 43f03a20e0fc..8b2eb3fb0189 100644 --- a/packages/SettingsLib/res/values-ur/strings.xml +++ b/packages/SettingsLib/res/values-ur/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"بیرونی آلہ"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"منسلک آلہ"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"یہ فون"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"اس آلے پر چلایا نہیں جا سکتا"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"سوئچ کرنے کے لیے اکاؤنٹ اپ گریڈ کریں"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"ڈاؤن لوڈز کو یہاں چلایا نہیں جا سکتا"</string> diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml index 685d6e63b082..62a6303fd45b 100644 --- a/packages/SettingsLib/res/values-uz/strings.xml +++ b/packages/SettingsLib/res/values-uz/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Tashqi qurilma"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Ulangan qurilma"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Shu telefon"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Bu qurilmada ijro etilmaydi"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Oʻtish uchun hisobingizni yangilang"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Yuklab olingan fayllar ijro etilmaydi"</string> diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml index 7e98afddbe10..1b90818fbaf9 100644 --- a/packages/SettingsLib/res/values-vi/strings.xml +++ b/packages/SettingsLib/res/values-vi/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Thiết bị bên ngoài"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Thiết bị đã kết nối"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Điện thoại này"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Không phát được trên thiết bị này"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Nâng cấp tài khoản để chuyển đổi"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Không thể phát các tệp đã tải xuống tại đây"</string> diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml index 0f3373e1142f..94915650223f 100644 --- a/packages/SettingsLib/res/values-zh-rCN/strings.xml +++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"外部设备"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"连接的设备"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"这部手机"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"无法在此设备上播放"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"升级账号后才能切换"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"无法在此设备上播放下载的内容"</string> diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml index c8666ff88a25..aa3ac06298c5 100644 --- a/packages/SettingsLib/res/values-zh-rHK/strings.xml +++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"外部裝置"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"已連接的裝置"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"這部手機"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"無法在此裝置上播放"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"請升級要切換的帳戶"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"無法在此播放下載內容"</string> diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml index 08bf732652b9..b5eb87da6de3 100644 --- a/packages/SettingsLib/res/values-zh-rTW/strings.xml +++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"外部裝置"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"已連結的裝置"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"這支手機"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"無法在這部裝置上播放"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"請升級要切換的帳戶"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"這裡無法播放下載內容"</string> diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml index ad4f04555d05..57e0b8d8afeb 100644 --- a/packages/SettingsLib/res/values-zu/strings.xml +++ b/packages/SettingsLib/res/values-zu/strings.xml @@ -591,6 +591,12 @@ <string name="media_transfer_external_device_name" msgid="2588672258721846418">"Idivayisi Yangaphandle"</string> <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Idivayisi exhunyiwe"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Le foni"</string> + <!-- no translation found for media_transfer_digital_line_name (312091711951124301) --> + <skip /> + <!-- no translation found for media_transfer_analog_line_name (1841163866716302104) --> + <skip /> + <!-- no translation found for media_transfer_aux_line_name (894135835967856689) --> + <skip /> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Ayikwazi ukudlala kule divayisi"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Thuthukisa i-akhawunti ukuze ushintshe"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Awukwazi ukudlala okudawunilodiwe lapha"</string> diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index bc144d60d238..f03014ca95e2 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -821,8 +821,10 @@ <!-- Title of checkbox setting that enables the Linux terminal app. [CHAR LIMIT=32] --> <string name="enable_linux_terminal_title">Linux development environment</string> - <!-- Summary of checkbox setting that enables the Linux terminal app. [CHAR LIMIT=64] --> - <string name="enable_linux_terminal_summary">Run Linux terminal on Android</string> + <!-- Summary of checkbox setting that enables the Linux terminal app. [CHAR LIMIT=none] --> + <string name="enable_linux_terminal_summary">(Experimental) Run Linux terminal on Android</string> + <!-- Disclaimer below the checkbox that disabling the Linux terminal app would clear its data. [CHAR LIMIT=none] --> + <string name="disable_linux_terminal_disclaimer">If you disable, Linux terminal data will be cleared</string> <!-- HDCP checking title, used for debug purposes only. [CHAR LIMIT=25] --> <string name="hdcp_checking_title">HDCP checking</string> diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java index 0dc772ab6ecd..ebd5a1deffd2 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java @@ -224,7 +224,7 @@ public class BluetoothEventManager { // audio sharing is enabled. if (bluetoothProfile == BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT && state == BluetoothAdapter.STATE_DISCONNECTED - && BluetoothUtils.isAudioSharingEnabled()) { + && BluetoothUtils.isAudioSharingUIAvailable(mContext)) { LocalBluetoothProfileManager profileManager = mBtManager.getProfileManager(); if (profileManager != null && profileManager.getLeAudioBroadcastProfile() != null diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java index 612c193da9c3..a87b8153b858 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java @@ -64,6 +64,8 @@ public class BluetoothUtils { public static final int META_INT_ERROR = -1; public static final String BT_ADVANCED_HEADER_ENABLED = "bt_advanced_header_enabled"; + public static final String DEVELOPER_OPTION_PREVIEW_KEY = + "bluetooth_le_audio_sharing_ui_preview_enabled"; private static final int METADATA_FAST_PAIR_CUSTOMIZED_FIELDS = 25; private static final String KEY_HEARABLE_CONTROL_SLICE = "HEARABLE_CONTROL_SLICE_WITH_WIDTH"; private static final Set<Integer> SA_PROFILES = @@ -643,6 +645,12 @@ public class BluetoothUtils { && connectedGroupIds.contains(groupId); } + /** Returns if the le audio sharing UI is available. */ + public static boolean isAudioSharingUIAvailable(@Nullable Context context) { + return isAudioSharingEnabled() || (context != null && isAudioSharingPreviewEnabled( + context.getContentResolver())); + } + /** Returns if the le audio sharing is enabled. */ public static boolean isAudioSharingEnabled() { BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); @@ -653,7 +661,23 @@ public class BluetoothUtils { && adapter.isLeAudioBroadcastAssistantSupported() == BluetoothStatusCodes.FEATURE_SUPPORTED; } catch (IllegalStateException e) { - Log.d(TAG, "LE state is on, but there is no bluetooth service.", e); + Log.d(TAG, "Fail to check isAudioSharingEnabled, e = ", e); + return false; + } + } + + /** Returns if the le audio sharing preview is enabled in developer option. */ + public static boolean isAudioSharingPreviewEnabled(@Nullable ContentResolver contentResolver) { + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + try { + return Flags.audioSharingDeveloperOption() + && getAudioSharingPreviewValue(contentResolver) + && adapter.isLeAudioBroadcastSourceSupported() + == BluetoothStatusCodes.FEATURE_SUPPORTED + && adapter.isLeAudioBroadcastAssistantSupported() + == BluetoothStatusCodes.FEATURE_SUPPORTED; + } catch (IllegalStateException e) { + Log.d(TAG, "Fail to check isAudioSharingPreviewEnabled, e = ", e); return false; } } @@ -996,6 +1020,17 @@ public class BluetoothUtils { BluetoothCsipSetCoordinator.GROUP_ID_INVALID); } + /** Get develop option value for audio sharing preview. */ + @WorkerThread + private static boolean getAudioSharingPreviewValue(@Nullable ContentResolver contentResolver) { + if (contentResolver == null) return false; + return Settings.Global.getInt( + contentResolver, + DEVELOPER_OPTION_PREVIEW_KEY, + 0 // value off + ) == 1; + } + /** Get secondary {@link CachedBluetoothDevice} in broadcast. */ @Nullable @WorkerThread diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java index 8641f7036c50..d0827b30efc9 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java @@ -1245,7 +1245,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> */ public String getConnectionSummary(boolean shortSummary) { CharSequence summary = null; - if (BluetoothUtils.isAudioSharingEnabled()) { + if (BluetoothUtils.isAudioSharingUIAvailable(mContext)) { if (mBluetoothManager == null) { mBluetoothManager = LocalBluetoothManager.getInstance(mContext, null); } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java index b9f16edf6e77..4b7cb36f2753 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java @@ -383,11 +383,7 @@ public class CsipDeviceManager { preferredMainDevice.refresh(); hasChanged = true; } - if (isWorkProfile()) { - log("addMemberDevicesIntoMainDevice: skip sync source for work profile"); - } else { - syncAudioSharingSourceIfNeeded(preferredMainDevice); - } + syncAudioSharingSourceIfNeeded(preferredMainDevice); } if (hasChanged) { log("addMemberDevicesIntoMainDevice: After changed, CachedBluetoothDevice list: " @@ -402,8 +398,12 @@ public class CsipDeviceManager { } private void syncAudioSharingSourceIfNeeded(CachedBluetoothDevice mainDevice) { - boolean isAudioSharingEnabled = BluetoothUtils.isAudioSharingEnabled(); + boolean isAudioSharingEnabled = BluetoothUtils.isAudioSharingUIAvailable(mContext); if (isAudioSharingEnabled) { + if (isWorkProfile()) { + log("addMemberDevicesIntoMainDevice: skip sync source for work profile"); + return; + } boolean hasBroadcastSource = BluetoothUtils.isBroadcasting(mBtManager) && BluetoothUtils.hasConnectedBroadcastSource( mainDevice, mBtManager); @@ -433,6 +433,8 @@ public class CsipDeviceManager { } } } + } else { + log("addMemberDevicesIntoMainDevice: skip sync source, flag disabled"); } } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingServiceConnection.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingServiceConnection.kt index edd49c5a8fb7..0209eb8c3fbf 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingServiceConnection.kt +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingServiceConnection.kt @@ -21,6 +21,7 @@ import android.content.ComponentName import android.content.Context import android.content.Intent import android.content.ServiceConnection +import android.os.DeadObjectException import android.os.IBinder import android.os.IInterface import android.os.RemoteException @@ -52,6 +53,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.callbackFlow +import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.emitAll import kotlinx.coroutines.flow.filterIsInstance @@ -304,6 +306,14 @@ class DeviceSettingServiceConnection( service.registerDeviceSettingsListener(deviceInfo, listener) awaitClose { service.unregisterDeviceSettingsListener(deviceInfo, listener) } } + .catch { e -> + if (e is DeadObjectException) { + Log.e(TAG, "DeadObjectException happens when registering listener.", e) + emit(listOf()) + } else { + throw e + } + } .stateIn(coroutineScope, SharingStarted.WhileSubscribed(), emptyList()) } diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/data/repository/ZenModeRepository.kt b/packages/SettingsLib/src/com/android/settingslib/notification/data/repository/ZenModeRepository.kt index 7fdbcdae2276..f446bb8e32d1 100644 --- a/packages/SettingsLib/src/com/android/settingslib/notification/data/repository/ZenModeRepository.kt +++ b/packages/SettingsLib/src/com/android/settingslib/notification/data/repository/ZenModeRepository.kt @@ -93,33 +93,23 @@ class ZenModeRepositoryImpl( IntentFilter().apply { addAction(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED) addAction(NotificationManager.ACTION_NOTIFICATION_POLICY_CHANGED) - if (Flags.volumePanelBroadcastFix() && android.app.Flags.modesApi()) + if (android.app.Flags.modesApi()) addAction( - NotificationManager.ACTION_CONSOLIDATED_NOTIFICATION_POLICY_CHANGED) + NotificationManager.ACTION_CONSOLIDATED_NOTIFICATION_POLICY_CHANGED + ) }, /* broadcastPermission = */ null, - /* scheduler = */ if (Flags.volumePanelBroadcastFix()) { - backgroundHandler - } else { - null - }, + /* scheduler = */ backgroundHandler, ) awaitClose { context.unregisterReceiver(receiver) } } - .let { - if (Flags.volumePanelBroadcastFix()) { - // Share the flow to avoid having multiple broadcasts. - it.flowOn(backgroundCoroutineContext) - .shareIn(started = SharingStarted.WhileSubscribed(), scope = scope) - } else { - it.shareIn(started = SharingStarted.WhileSubscribed(), scope = scope) - } - } + .flowOn(backgroundCoroutineContext) + .shareIn(started = SharingStarted.WhileSubscribed(), scope = scope) } override val consolidatedNotificationPolicy: StateFlow<NotificationManager.Policy?> by lazy { - if (Flags.volumePanelBroadcastFix() && android.app.Flags.modesApi()) + if (android.app.Flags.modesApi()) flowFromBroadcast(NotificationManager.ACTION_CONSOLIDATED_NOTIFICATION_POLICY_CHANGED) { // If available, get the value from extras to avoid a potential binder call. it?.extras?.getParcelable(EXTRA_NOTIFICATION_POLICY) @@ -161,11 +151,13 @@ class ZenModeRepositoryImpl( contentResolver.registerContentObserver( Settings.Global.getUriFor(Settings.Global.ZEN_MODE), /* notifyForDescendants= */ false, - observer) + observer, + ) contentResolver.registerContentObserver( Settings.Global.getUriFor(Settings.Global.ZEN_MODE_CONFIG_ETAG), /* notifyForDescendants= */ false, - observer) + observer, + ) awaitClose { contentResolver.unregisterContentObserver(observer) } } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java index 0e060dfdd447..6d481dbe64e9 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java @@ -29,11 +29,13 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothCsipSetCoordinator; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothLeBroadcastReceiveState; import android.bluetooth.BluetoothProfile; +import android.bluetooth.BluetoothStatusCodes; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; @@ -48,6 +50,7 @@ import android.util.Pair; import com.android.internal.R; import com.android.settingslib.flags.Flags; +import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter; import com.android.settingslib.widget.AdaptiveIcon; import com.google.common.collect.ImmutableList; @@ -61,6 +64,8 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; +import org.robolectric.shadow.api.Shadow; import java.util.ArrayList; import java.util.Collections; @@ -69,6 +74,7 @@ import java.util.List; import java.util.Set; @RunWith(RobolectricTestRunner.class) +@Config(shadows = {ShadowBluetoothAdapter.class}) public class BluetoothUtilsTest { @Mock(answer = Answers.RETURNS_DEEP_STUBS) @@ -88,6 +94,7 @@ public class BluetoothUtilsTest { @Mock private BluetoothLeBroadcastReceiveState mLeBroadcastReceiveState; private Context mContext; + private ShadowBluetoothAdapter mShadowBluetoothAdapter; private static final String STRING_METADATA = "string_metadata"; private static final String BOOL_METADATA = "true"; private static final String INT_METADATA = "25"; @@ -109,6 +116,7 @@ public class BluetoothUtilsTest { mContext = spy(RuntimeEnvironment.application); mSetFlagsRule.disableFlags(FLAG_ENABLE_DETERMINING_ADVANCED_DETAILS_HEADER_WITH_METADATA); + mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter()); when(mLocalBluetoothManager.getProfileManager()).thenReturn(mProfileManager); when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(mDeviceManager); when(mProfileManager.getLeAudioBroadcastProfile()).thenReturn(mBroadcast); @@ -1123,4 +1131,129 @@ public class BluetoothUtilsTest { AudioDeviceInfo.TYPE_HEARING_AID, address)); } + + @Test + public void isAudioSharingEnabled_flagOff_returnsFalse() { + mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); + + assertThat(BluetoothUtils.isAudioSharingEnabled()).isFalse(); + } + + @Test + public void isAudioSharingEnabled_featureNotSupported_returnsFalse() { + mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); + mShadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported( + BluetoothStatusCodes.FEATURE_NOT_SUPPORTED); + mShadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported( + BluetoothStatusCodes.FEATURE_SUPPORTED); + + assertThat(BluetoothUtils.isAudioSharingEnabled()).isFalse(); + } + + @Test + public void isAudioSharingEnabled_featureSupported_returnsTrue() { + mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); + mShadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported( + BluetoothStatusCodes.FEATURE_SUPPORTED); + mShadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported( + BluetoothStatusCodes.FEATURE_SUPPORTED); + + assertThat(BluetoothUtils.isAudioSharingEnabled()).isTrue(); + } + + @Test + public void isAudioSharingPreviewEnabled_flagOff_returnsFalse() { + mSetFlagsRule.disableFlags(Flags.FLAG_AUDIO_SHARING_DEVELOPER_OPTION); + + assertThat(BluetoothUtils.isAudioSharingPreviewEnabled( + mContext.getContentResolver())).isFalse(); + } + + @Test + public void isAudioSharingPreviewEnabled_featureNotSupported_returnsFalse() { + mSetFlagsRule.disableFlags(Flags.FLAG_AUDIO_SHARING_DEVELOPER_OPTION); + mShadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported( + BluetoothStatusCodes.FEATURE_NOT_SUPPORTED); + mShadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported( + BluetoothStatusCodes.FEATURE_SUPPORTED); + + assertThat(BluetoothUtils.isAudioSharingPreviewEnabled( + mContext.getContentResolver())).isFalse(); + } + + @Test + public void isAudioSharingPreviewEnabled_developerOptionOff_returnsFalse() { + mSetFlagsRule.enableFlags(Flags.FLAG_AUDIO_SHARING_DEVELOPER_OPTION); + mShadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported( + BluetoothStatusCodes.FEATURE_SUPPORTED); + mShadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported( + BluetoothStatusCodes.FEATURE_SUPPORTED); + Settings.Global.putInt(mContext.getContentResolver(), + BluetoothUtils.DEVELOPER_OPTION_PREVIEW_KEY, 0); + + assertThat(BluetoothUtils.isAudioSharingPreviewEnabled( + mContext.getContentResolver())).isFalse(); + } + + @Test + public void isAudioSharingPreviewEnabled_developerOptionOn_returnsTrue() { + mSetFlagsRule.enableFlags(Flags.FLAG_AUDIO_SHARING_DEVELOPER_OPTION); + mShadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported( + BluetoothStatusCodes.FEATURE_SUPPORTED); + mShadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported( + BluetoothStatusCodes.FEATURE_SUPPORTED); + Settings.Global.putInt(mContext.getContentResolver(), + BluetoothUtils.DEVELOPER_OPTION_PREVIEW_KEY, 1); + + assertThat(BluetoothUtils.isAudioSharingPreviewEnabled( + mContext.getContentResolver())).isTrue(); + } + + @Test + public void isAudioSharingUIAvailable_audioSharingAndPreviewFlagOff_returnsFalse() { + mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); + mSetFlagsRule.disableFlags(Flags.FLAG_AUDIO_SHARING_DEVELOPER_OPTION); + + assertThat(BluetoothUtils.isAudioSharingUIAvailable(mContext)).isFalse(); + } + + @Test + public void isAudioSharingUIAvailable_audioSharingAndPreviewDisabled_returnsFalse() { + mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); + mSetFlagsRule.enableFlags(Flags.FLAG_AUDIO_SHARING_DEVELOPER_OPTION); + mShadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported( + BluetoothStatusCodes.FEATURE_NOT_SUPPORTED); + mShadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported( + BluetoothStatusCodes.FEATURE_SUPPORTED); + + assertThat(BluetoothUtils.isAudioSharingUIAvailable(mContext)).isFalse(); + } + + @Test + public void isAudioSharingUIAvailable_audioSharingEnabled_returnsTrue() { + mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); + mShadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported( + BluetoothStatusCodes.FEATURE_SUPPORTED); + mShadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported( + BluetoothStatusCodes.FEATURE_SUPPORTED); + mSetFlagsRule.enableFlags(Flags.FLAG_AUDIO_SHARING_DEVELOPER_OPTION); + Settings.Global.putInt(mContext.getContentResolver(), + BluetoothUtils.DEVELOPER_OPTION_PREVIEW_KEY, 0); + + assertThat(BluetoothUtils.isAudioSharingUIAvailable(mContext)).isTrue(); + } + + @Test + public void isAudioSharingUIAvailable_audioSharingPreviewEnabled_returnsTrue() { + mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); + mShadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported( + BluetoothStatusCodes.FEATURE_SUPPORTED); + mShadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported( + BluetoothStatusCodes.FEATURE_SUPPORTED); + mSetFlagsRule.enableFlags(Flags.FLAG_AUDIO_SHARING_DEVELOPER_OPTION); + Settings.Global.putInt(mContext.getContentResolver(), + BluetoothUtils.DEVELOPER_OPTION_PREVIEW_KEY, 1); + + assertThat(BluetoothUtils.isAudioSharingUIAvailable(mContext)).isTrue(); + } } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/data/repository/ZenModeRepositoryTest.kt b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/data/repository/ZenModeRepositoryTest.kt index c136644e0959..388af61c6273 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/data/repository/ZenModeRepositoryTest.kt +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/data/repository/ZenModeRepositoryTest.kt @@ -23,12 +23,10 @@ import android.content.Context import android.content.Intent import android.database.ContentObserver import android.os.Parcelable -import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import android.platform.test.flag.junit.SetFlagsRule import android.provider.Settings.Global import androidx.test.filters.SmallTest -import com.android.settingslib.flags.Flags import com.android.settingslib.notification.modes.TestModeBuilder import com.android.settingslib.notification.modes.ZenMode import com.android.settingslib.notification.modes.ZenModesBackend @@ -93,26 +91,7 @@ class ZenModeRepositoryTest { ) } - @DisableFlags(Flags.FLAG_VOLUME_PANEL_BROADCAST_FIX) - @Test - fun consolidatedPolicyChanges_repositoryEmits_flagsOff() { - testScope.runTest { - val values = mutableListOf<NotificationManager.Policy?>() - `when`(notificationManager.consolidatedNotificationPolicy).thenReturn(testPolicy1) - underTest.consolidatedNotificationPolicy - .onEach { values.add(it) } - .launchIn(backgroundScope) - runCurrent() - - `when`(notificationManager.consolidatedNotificationPolicy).thenReturn(testPolicy2) - triggerIntent(NotificationManager.ACTION_NOTIFICATION_POLICY_CHANGED) - runCurrent() - - assertThat(values).containsExactly(null, testPolicy1, testPolicy2).inOrder() - } - } - - @EnableFlags(android.app.Flags.FLAG_MODES_API, Flags.FLAG_VOLUME_PANEL_BROADCAST_FIX) + @EnableFlags(android.app.Flags.FLAG_MODES_API) @Test fun consolidatedPolicyChanges_repositoryEmits_flagsOn() { testScope.runTest { @@ -131,7 +110,7 @@ class ZenModeRepositoryTest { } } - @EnableFlags(android.app.Flags.FLAG_MODES_API, Flags.FLAG_VOLUME_PANEL_BROADCAST_FIX) + @EnableFlags(android.app.Flags.FLAG_MODES_API) @Test fun consolidatedPolicyChanges_repositoryEmitsFromExtras() { testScope.runTest { diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java index 064198fc5e46..927a1c59cc76 100644 --- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java +++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java @@ -284,5 +284,6 @@ public class SecureSettings { Settings.Secure.MANDATORY_BIOMETRICS, Settings.Secure.MANDATORY_BIOMETRICS_REQUIREMENTS_SATISFIED, Settings.Secure.ADVANCED_PROTECTION_MODE, + Settings.Secure.ACCESSIBILITY_KEY_GESTURE_TARGETS, }; } diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java index c002a04d5b11..6d73ee27f076 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java @@ -332,6 +332,9 @@ public class SecureSettingsValidators { VALIDATORS.put( Secure.ACCESSIBILITY_QS_TARGETS, ACCESSIBILITY_SHORTCUT_TARGET_LIST_VALIDATOR); + VALIDATORS.put( + Secure.ACCESSIBILITY_KEY_GESTURE_TARGETS, + ACCESSIBILITY_SHORTCUT_TARGET_LIST_VALIDATOR); VALIDATORS.put(Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.ONE_HANDED_MODE_ACTIVATED, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.ONE_HANDED_MODE_ENABLED, BOOLEAN_VALIDATOR); diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java index fbce6ca07b3e..aca2c4ef2a49 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java @@ -16,6 +16,7 @@ package com.android.providers.settings; +import static android.provider.DeviceConfig.DUMP_ARG_NAMESPACE; import static android.provider.Settings.Config.SYNC_DISABLED_MODE_NONE; import static android.provider.Settings.Config.SYNC_DISABLED_MODE_PERSISTENT; import static android.provider.Settings.Config.SYNC_DISABLED_MODE_UNTIL_REBOOT; @@ -42,6 +43,7 @@ import android.provider.DeviceConfigShellCommandHandler; import android.provider.Settings; import android.provider.Settings.Config.SyncDisabledMode; import android.provider.UpdatableDeviceConfigServiceReadiness; +import android.util.Log; import android.util.Slog; import com.android.internal.util.FastPrintWriter; @@ -55,11 +57,13 @@ import java.io.PrintWriter; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.regex.Pattern; /** * Receives shell commands from the command line related to device config flags, and dispatches them @@ -80,6 +84,7 @@ public final class DeviceConfigService extends Binder { final SettingsProvider mProvider; private static final String TAG = "DeviceConfigService"; + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); public DeviceConfigService(SettingsProvider provider) { mProvider = provider; @@ -97,14 +102,35 @@ public final class DeviceConfigService extends Binder { } } + // TODO(b/364399200): add unit test @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + String filter = null; if (android.provider.flags.Flags.dumpImprovements()) { - pw.print("SyncDisabledForTests: "); - MyShellCommand.getSyncDisabledForTests(pw, pw); + if (args.length > 0) { + switch (args[0]) { + case DUMP_ARG_NAMESPACE: + if (args.length < 2) { + throw new IllegalArgumentException("argument " + DUMP_ARG_NAMESPACE + + " requires an extra argument"); + } + filter = args[1]; + if (DEBUG) { + Slog.d(TAG, "dump(): setting filter as " + filter); + } + break; + default: + Slog.w(TAG, "dump(): ignoring invalid arguments: " + Arrays.toString(args)); + break; + } + } + if (filter == null) { + pw.print("SyncDisabledForTests: "); + MyShellCommand.getSyncDisabledForTests(pw, pw); - pw.print("UpdatableDeviceConfigServiceReadiness.shouldStartUpdatableService(): "); - pw.println(UpdatableDeviceConfigServiceReadiness.shouldStartUpdatableService()); + pw.print("UpdatableDeviceConfigServiceReadiness.shouldStartUpdatableService(): "); + pw.println(UpdatableDeviceConfigServiceReadiness.shouldStartUpdatableService()); + } pw.println("DeviceConfig provider: "); try (ParcelFileDescriptor pfd = ParcelFileDescriptor.dup(fd)) { @@ -117,8 +143,16 @@ public final class DeviceConfigService extends Binder { IContentProvider iprovider = mProvider.getIContentProvider(); pw.println("DeviceConfig flags:"); + Pattern lineFilter = filter == null ? null : Pattern.compile("^.*" + filter + ".*\\/.*$"); for (String line : MyShellCommand.listAll(iprovider)) { - pw.println(line); + if (lineFilter == null || lineFilter.matcher(line).matches()) { + pw.println(line); + } + } + + if (filter != null) { + // TODO(b/364399200): use filter to skip instead? + return; } ArrayList<String> missingFiles = new ArrayList<String>(); diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java index 2034f36c558b..fb0aaf8e5ae1 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java @@ -1823,6 +1823,9 @@ class SettingsProtoDumpUtil { Settings.Secure.ACCESSIBILITY_QS_TARGETS, SecureSettingsProto.Accessibility.QS_TARGETS); dumpSetting(s, p, + Settings.Secure.ACCESSIBILITY_KEY_GESTURE_TARGETS, + SecureSettingsProto.Accessibility.ACCESSIBILITY_KEY_GESTURE_TARGETS); + dumpSetting(s, p, Settings.Secure.ACCESSIBILITY_MAGNIFICATION_CAPABILITY, SecureSettingsProto.Accessibility.ACCESSIBILITY_MAGNIFICATION_CAPABILITY); dumpSetting(s, p, diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 24b91f9ab436..7b6321d1cc7d 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -959,6 +959,13 @@ <!-- Permission required for CTS test - CtsTelephonyTestCases --> <uses-permission android:name="android.permission.READ_BASIC_PHONE_STATE" /> + <!-- Permission required for ExecutableMethodFileOffsetsTest --> + <uses-permission android:name="android.permission.DYNAMIC_INSTRUMENTATION" /> + + <!-- Permissions required for CTS test - SettingsPreferenceServiceClientTest --> + <uses-permission android:name="android.permission.READ_SYSTEM_PREFERENCES" /> + <uses-permission android:name="android.permission.WRITE_SYSTEM_PREFERENCES" /> + <application android:label="@string/app_label" android:theme="@android:style/Theme.DeviceDefault.DayNight" diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java index b4d81d6937ed..7c478ac78a29 100644 --- a/packages/Shell/src/com/android/shell/BugreportProgressService.java +++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java @@ -353,7 +353,7 @@ public class BugreportProgressService extends Service { public void onDestroy() { mServiceHandler.getLooper().quit(); mScreenshotHandler.getLooper().quit(); - mBugreportSingleThreadExecutor.close(); + mBugreportSingleThreadExecutor.shutdown(); super.onDestroy(); } diff --git a/packages/SystemUI/README.md b/packages/SystemUI/README.md index 2910bba71341..635a97ef8a47 100644 --- a/packages/SystemUI/README.md +++ b/packages/SystemUI/README.md @@ -87,7 +87,7 @@ immediately for any callbacks added. There are a few places where CommandQueue is used as a bus to communicate across sysui. Such as when StatusBar calls CommandQueue#recomputeDisableFlags. -This is generally used a shortcut to directly trigger CommandQueue rather than +This is generally used as a shortcut to directly trigger CommandQueue rather than calling StatusManager and waiting for the call to come back to IStatusBar. ### [com.android.systemui.util.NotificationChannels](/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java) diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig index 5eae6d3e43fe..a4b8821383e0 100644 --- a/packages/SystemUI/aconfig/systemui.aconfig +++ b/packages/SystemUI/aconfig/systemui.aconfig @@ -16,6 +16,16 @@ flag { } flag { + name: "multiuser_wifi_picker_tracker_support" + namespace: "systemui" + description: "Adds WifiPickerTracker support for multiple users to support when HSUM is enabled." + bug: "371586248" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { name: "udfps_view_performance" namespace: "systemui" description: "Decrease screen off blocking calls by waiting until the device is finished going to sleep before adding the udfps view." @@ -257,7 +267,7 @@ flag { flag { name: "dual_shade" namespace: "systemui" - description: "Enables the BC25 Dual Shade (go/bc25-dual-shade-design)." + description: "Enables Dual Shade (go/dual-shade-design-doc)." bug: "337259436" } @@ -548,6 +558,13 @@ flag { } flag { + name: "lockscreen_custom_clocks" + namespace: "systemui" + description: "Enable lockscreen custom clocks" + bug: "378486437" +} + +flag { name: "faster_unlock_transition" namespace: "systemui" description: "Faster wallpaper unlock transition" @@ -631,9 +648,9 @@ flag { } flag { - name: "status_bar_simple_fragment" + name: "status_bar_root_modernization" namespace: "systemui" - description: "Feature flag for refactoring the collapsed status bar fragment" + description: "Feature flag for replacing the status bar fragment with a compose root" bug: "364360986" } @@ -1350,16 +1367,6 @@ flag { } flag { - name: "notification_pulsing_fix" - namespace: "systemui" - description: "Allow showing new pulsing notifications when the device is already pulsing." - bug: "335560575" - metadata { - purpose: PURPOSE_BUGFIX - } -} - -flag { name: "media_lockscreen_launch_animation" namespace : "systemui" description : "Enable the origin launch animation for UMO when opening on top of lockscreen." @@ -1774,3 +1781,13 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "keyguard_transition_force_finish_on_screen_off" + namespace: "systemui" + description: "Forces KTF transitions to finish if the screen turns all the way off." + bug: "331636736" + metadata { + purpose: PURPOSE_BUGFIX + } +}
\ No newline at end of file diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt index 9392b1afffa3..96e99b15363d 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt @@ -66,13 +66,13 @@ constructor( interactionHandler = interactionHandler, dialogFactory = dialogFactory, widgetSection = widgetSection, - modifier = Modifier.element(Communal.Elements.Grid) + modifier = Modifier.element(Communal.Elements.Grid), ) } with(lockSection) { LockIcon( overrideColor = MaterialTheme.colorScheme.onPrimaryContainer, - modifier = Modifier.element(Communal.Elements.LockIcon) + modifier = Modifier.element(Communal.Elements.LockIcon), ) } with(bottomAreaSection) { @@ -80,17 +80,13 @@ constructor( Modifier.element(Communal.Elements.IndicationArea).fillMaxWidth() ) } - } + }, ) { measurables, constraints -> val communalGridMeasurable = measurables[0] val lockIconMeasurable = measurables[1] val bottomAreaMeasurable = measurables[2] - val noMinConstraints = - constraints.copy( - minWidth = 0, - minHeight = 0, - ) + val noMinConstraints = constraints.copy(minWidth = 0, minHeight = 0) val lockIconPlaceable = lockIconMeasurable.measure(noMinConstraints) val lockIconBounds = @@ -109,14 +105,8 @@ constructor( ) layout(constraints.maxWidth, constraints.maxHeight) { - communalGridPlaceable.place( - x = 0, - y = 0, - ) - lockIconPlaceable.place( - x = lockIconBounds.left, - y = lockIconBounds.top, - ) + communalGridPlaceable.place(x = 0, y = 0) + lockIconPlaceable.place(x = lockIconBounds.left, y = lockIconBounds.top) bottomAreaPlaceable.place( x = 0, y = constraints.maxHeight - bottomAreaPlaceable.height, 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 5e1ac1f30354..df1185cb1a6d 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 @@ -807,7 +807,6 @@ private fun BoxScope.CommunalHubLazyGrid( ) { ResizeableItemFrameViewModel() } - if (viewModel.isEditMode && dragDropState != null) { val isItemDragging = dragDropState.draggingItemKey == item.key val outlineAlpha by diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ContentListState.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ContentListState.kt index 6e30575a684d..16002bc709fd 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ContentListState.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ContentListState.kt @@ -59,7 +59,14 @@ internal constructor( private val onAddWidget: (componentName: ComponentName, user: UserHandle, rank: Int) -> Unit, private val onDeleteWidget: (id: Int, componentName: ComponentName, rank: Int) -> Unit, private val onReorderWidgets: (widgetIdToRankMap: Map<Int, Int>) -> Unit, - private val onResizeWidget: (id: Int, spanY: Int, widgetIdToRankMap: Map<Int, Int>) -> Unit, + private val onResizeWidget: + ( + id: Int, + spanY: Int, + widgetIdToRankMap: Map<Int, Int>, + componentName: ComponentName, + rank: Int, + ) -> Unit, ) { var list = communalContent.toMutableStateList() private set @@ -105,7 +112,9 @@ internal constructor( } else { emptyMap() } - onResizeWidget(item.appWidgetId, newSpan, widgetIdToRankMap) + val componentName = item.componentName + val rank = item.rank + onResizeWidget(item.appWidgetId, newSpan, widgetIdToRankMap, componentName, rank) } /** diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/statusbar/phone/SystemUIDialogFactoryExt.kt b/packages/SystemUI/compose/features/src/com/android/systemui/statusbar/phone/SystemUIDialogFactoryExt.kt index d58e1bfbda83..163f4b36f472 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/statusbar/phone/SystemUIDialogFactoryExt.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/statusbar/phone/SystemUIDialogFactoryExt.kt @@ -59,6 +59,7 @@ import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.hideFromAccessibility import androidx.compose.ui.semantics.semantics import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.IntOffset @@ -286,9 +287,12 @@ private fun DragHandle(dialog: Dialog) { Surface( modifier = Modifier.padding(top = 16.dp, bottom = 6.dp) - .semantics { contentDescription = dragHandleContentDescription } + .semantics { + contentDescription = dragHandleContentDescription + hideFromAccessibility() + } .clickable { dialog.dismiss() }, - color = MaterialTheme.colorScheme.outlineVariant, + color = MaterialTheme.colorScheme.onSurfaceVariant, shape = MaterialTheme.shapes.extraLarge, ) { Box(Modifier.size(width = 32.dp, height = 4.dp)) diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt index 041cd15bdeea..c33d655fe52b 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt @@ -25,7 +25,6 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.round import androidx.compose.ui.util.fastCoerceIn import com.android.compose.animation.scene.content.Content -import com.android.compose.animation.scene.content.state.TransitionState import com.android.compose.animation.scene.content.state.TransitionState.HasOverscrollProperties.Companion.DistanceUnspecified import com.android.compose.nestedscroll.OnStopScope import com.android.compose.nestedscroll.PriorityNestedScrollConnection @@ -36,11 +35,11 @@ internal typealias SuspendedValue<T> = suspend () -> T internal interface DraggableHandler { /** - * Start a drag with the given [pointersInfo] and [overSlop]. + * Start a drag with the given [pointersDown] and [overSlop]. * * The returned [DragController] should be used to continue or stop the drag. */ - fun onDragStarted(pointersInfo: PointersInfo?, overSlop: Float): DragController + fun onDragStarted(pointersDown: PointersInfo.PointersDown?, overSlop: Float): DragController } /** @@ -95,7 +94,7 @@ internal class DraggableHandlerImpl( * Note: if this returns true, then [onDragStarted] will be called with overSlop equal to 0f, * indicating that the transition should be intercepted. */ - internal fun shouldImmediatelyIntercept(pointersInfo: PointersInfo?): Boolean { + internal fun shouldImmediatelyIntercept(pointersDown: PointersInfo.PointersDown?): Boolean { // We don't intercept the touch if we are not currently driving the transition. val dragController = dragController if (dragController?.isDrivingTransition != true) { @@ -106,7 +105,7 @@ internal class DraggableHandlerImpl( // Only intercept the current transition if one of the 2 swipes results is also a transition // between the same pair of contents. - val swipes = computeSwipes(pointersInfo) + val swipes = computeSwipes(pointersDown) val fromContent = layoutImpl.content(swipeAnimation.currentContent) val (upOrLeft, downOrRight) = swipes.computeSwipesResults(fromContent) val currentScene = layoutImpl.state.currentScene @@ -123,7 +122,10 @@ internal class DraggableHandlerImpl( )) } - override fun onDragStarted(pointersInfo: PointersInfo?, overSlop: Float): DragController { + override fun onDragStarted( + pointersDown: PointersInfo.PointersDown?, + overSlop: Float, + ): DragController { if (overSlop == 0f) { val oldDragController = dragController check(oldDragController != null && oldDragController.isDrivingTransition) { @@ -148,7 +150,7 @@ internal class DraggableHandlerImpl( return updateDragController(swipes, swipeAnimation) } - val swipes = computeSwipes(pointersInfo) + val swipes = computeSwipes(pointersDown) val fromContent = layoutImpl.contentForUserActions() swipes.updateSwipesResults(fromContent) @@ -194,11 +196,11 @@ internal class DraggableHandlerImpl( ) } - private fun computeSwipes(pointersInfo: PointersInfo?): Swipes { - val fromSource = pointersInfo?.let { resolveSwipeSource(it.startedPosition) } + private fun computeSwipes(pointersDown: PointersInfo.PointersDown?): Swipes { + val fromSource = pointersDown?.let { resolveSwipeSource(it.startedPosition) } return Swipes( - upOrLeft = resolveSwipe(orientation, isUpOrLeft = true, pointersInfo, fromSource), - downOrRight = resolveSwipe(orientation, isUpOrLeft = false, pointersInfo, fromSource), + upOrLeft = resolveSwipe(orientation, isUpOrLeft = true, pointersDown, fromSource), + downOrRight = resolveSwipe(orientation, isUpOrLeft = false, pointersDown, fromSource), ) } } @@ -206,7 +208,7 @@ internal class DraggableHandlerImpl( private fun resolveSwipe( orientation: Orientation, isUpOrLeft: Boolean, - pointersInfo: PointersInfo?, + pointersDown: PointersInfo.PointersDown?, fromSource: SwipeSource.Resolved?, ): Swipe.Resolved { return Swipe.Resolved( @@ -227,9 +229,9 @@ private fun resolveSwipe( } }, // If the number of pointers is not specified, 1 is assumed. - pointerCount = pointersInfo?.pointersDown ?: 1, + pointerCount = pointersDown?.count ?: 1, // Resolves the pointer type only if all pointers are of the same type. - pointersType = pointersInfo?.pointersDownByType?.keys?.singleOrNull(), + pointersType = pointersDown?.countByType?.keys?.singleOrNull(), fromSource = fromSource, ) } @@ -540,40 +542,15 @@ internal class NestedScrollHandlerImpl( val connection: PriorityNestedScrollConnection = nestedScrollConnection() - private fun resolveSwipe(isUpOrLeft: Boolean, pointersInfo: PointersInfo?): Swipe.Resolved { - return resolveSwipe( - orientation = draggableHandler.orientation, - isUpOrLeft = isUpOrLeft, - pointersInfo = pointersInfo, - fromSource = - pointersInfo?.let { draggableHandler.resolveSwipeSource(it.startedPosition) }, - ) - } - private fun nestedScrollConnection(): PriorityNestedScrollConnection { // If we performed a long gesture before entering priority mode, we would have to avoid // moving on to the next scene. var canChangeScene = false - var lastPointersInfo: PointersInfo? = null - - fun hasNextScene(amount: Float): Boolean { - val transitionState = layoutState.transitionState - val scene = transitionState.currentScene - val fromScene = layoutImpl.scene(scene) - val resolvedSwipe = - when { - amount < 0f -> resolveSwipe(isUpOrLeft = true, lastPointersInfo) - amount > 0f -> resolveSwipe(isUpOrLeft = false, lastPointersInfo) - else -> null - } - val nextScene = resolvedSwipe?.let { fromScene.findActionResultBestMatch(it) } - if (nextScene != null) return true - - if (transitionState !is TransitionState.Idle) return false + var lastPointersDown: PointersInfo.PointersDown? = null - val overscrollSpec = layoutImpl.state.transitions.overscrollSpec(scene, orientation) - return overscrollSpec != null + fun shouldEnableSwipes(): Boolean { + return layoutImpl.contentForUserActions().shouldEnableSwipes(orientation) } var isIntercepting = false @@ -581,14 +558,24 @@ internal class NestedScrollHandlerImpl( return PriorityNestedScrollConnection( orientation = orientation, canStartPreScroll = { offsetAvailable, offsetBeforeStart, _ -> - val pointersInfo = pointersInfoOwner.pointersInfo() + val pointersDown: PointersInfo.PointersDown? = + when (val info = pointersInfoOwner.pointersInfo()) { + PointersInfo.MouseWheel -> { + // Do not support mouse wheel interactions + return@PriorityNestedScrollConnection false + } + + is PointersInfo.PointersDown -> info + null -> null + } + canChangeScene = if (isExternalOverscrollGesture()) false else offsetBeforeStart == 0f val canInterceptSwipeTransition = canChangeScene && offsetAvailable != 0f && - draggableHandler.shouldImmediatelyIntercept(pointersInfo) + draggableHandler.shouldImmediatelyIntercept(pointersDown) if (!canInterceptSwipeTransition) return@PriorityNestedScrollConnection false val threshold = layoutImpl.transitionInterceptionThreshold @@ -599,11 +586,7 @@ internal class NestedScrollHandlerImpl( return@PriorityNestedScrollConnection false } - if (pointersInfo?.isMouseWheel == true) { - // Do not support mouse wheel interactions - return@PriorityNestedScrollConnection false - } - lastPointersInfo = pointersInfo + lastPointersDown = pointersDown // If the current swipe transition is *not* closed to 0f or 1f, then we want the // scroll events to intercept the current transition to continue the scene @@ -622,28 +605,33 @@ internal class NestedScrollHandlerImpl( val isZeroOffset = if (isExternalOverscrollGesture()) false else offsetBeforeStart == 0f - val pointersInfo = pointersInfoOwner.pointersInfo() - if (pointersInfo?.isMouseWheel == true) { - // Do not support mouse wheel interactions - return@PriorityNestedScrollConnection false - } - lastPointersInfo = pointersInfo + val pointersDown: PointersInfo.PointersDown? = + when (val info = pointersInfoOwner.pointersInfo()) { + PointersInfo.MouseWheel -> { + // Do not support mouse wheel interactions + return@PriorityNestedScrollConnection false + } + + is PointersInfo.PointersDown -> info + null -> null + } + lastPointersDown = pointersDown val canStart = when (behavior) { NestedScrollBehavior.EdgeNoPreview -> { canChangeScene = isZeroOffset - isZeroOffset && hasNextScene(offsetAvailable) + isZeroOffset && shouldEnableSwipes() } NestedScrollBehavior.EdgeWithPreview -> { canChangeScene = isZeroOffset - hasNextScene(offsetAvailable) + shouldEnableSwipes() } NestedScrollBehavior.EdgeAlways -> { canChangeScene = true - hasNextScene(offsetAvailable) + shouldEnableSwipes() } } @@ -664,14 +652,19 @@ internal class NestedScrollHandlerImpl( // We could start an overscroll animation canChangeScene = false - val pointersInfo = pointersInfoOwner.pointersInfo() - if (pointersInfo?.isMouseWheel == true) { - // Do not support mouse wheel interactions - return@PriorityNestedScrollConnection false - } - lastPointersInfo = pointersInfo + val pointersDown: PointersInfo.PointersDown? = + when (val info = pointersInfoOwner.pointersInfo()) { + PointersInfo.MouseWheel -> { + // Do not support mouse wheel interactions + return@PriorityNestedScrollConnection false + } + + is PointersInfo.PointersDown -> info + null -> null + } + lastPointersDown = pointersDown - val canStart = behavior.canStartOnPostFling && hasNextScene(velocityAvailable) + val canStart = behavior.canStartOnPostFling && shouldEnableSwipes() if (canStart) { isIntercepting = false } @@ -679,11 +672,10 @@ internal class NestedScrollHandlerImpl( canStart }, onStart = { firstScroll -> - val pointersInfo = lastPointersInfo scrollController( dragController = draggableHandler.onDragStarted( - pointersInfo = pointersInfo, + pointersDown = lastPointersDown, overSlop = if (isIntercepting) 0f else firstScroll, ), canChangeScene = canChangeScene, @@ -701,8 +693,7 @@ private fun scrollController( ): ScrollController { return object : ScrollController { override fun onScroll(deltaScroll: Float, source: NestedScrollSource): Float { - val pointersInfo = pointersInfoOwner.pointersInfo() - if (pointersInfo?.isMouseWheel == true) { + if (pointersInfoOwner.pointersInfo() == PointersInfo.MouseWheel) { // Do not support mouse wheel interactions return 0f } diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt index e7b66c5f0d2f..d976e8e62f3c 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt @@ -693,8 +693,8 @@ private fun prepareInterruption( val fromState = updateStateInContent(transition.fromContent) val toState = updateStateInContent(transition.toContent) - reconcileStates(element, previousTransition) - reconcileStates(element, transition) + val previousUniqueState = reconcileStates(element, previousTransition, previousState = null) + reconcileStates(element, transition, previousState = previousUniqueState) // Remove the interruption values to all contents but the content(s) where the element will be // placed, to make sure that interruption deltas are computed only right after this interruption @@ -721,12 +721,32 @@ private fun prepareInterruption( /** * Reconcile the state of [element] in the formContent and toContent of [transition] so that the * values before interruption have their expected values, taking shared transitions into account. + * + * @return the unique state this element had during [transition], `null` if it had multiple + * different states (i.e. the shared animation was disabled). */ -private fun reconcileStates(element: Element, transition: TransitionState.Transition) { - val fromContentState = element.stateByContent[transition.fromContent] ?: return - val toContentState = element.stateByContent[transition.toContent] ?: return +private fun reconcileStates( + element: Element, + transition: TransitionState.Transition, + previousState: Element.State?, +): Element.State? { + fun reconcileWithPreviousState(state: Element.State) { + if (previousState != null && state.offsetBeforeInterruption == Offset.Unspecified) { + state.updateValuesBeforeInterruption(previousState) + } + } + + val fromContentState = element.stateByContent[transition.fromContent] + val toContentState = element.stateByContent[transition.toContent] + + if (fromContentState == null || toContentState == null) { + return (fromContentState ?: toContentState) + ?.also { reconcileWithPreviousState(it) } + ?.takeIf { it.offsetBeforeInterruption != Offset.Unspecified } + } + if (!isSharedElementEnabled(element.key, transition)) { - return + return null } if ( @@ -735,13 +755,19 @@ private fun reconcileStates(element: Element, transition: TransitionState.Transi ) { // Element is shared and placed in fromContent only. toContentState.updateValuesBeforeInterruption(fromContentState) - } else if ( + return fromContentState + } + + if ( toContentState.offsetBeforeInterruption != Offset.Unspecified && fromContentState.offsetBeforeInterruption == Offset.Unspecified ) { // Element is shared and placed in toContent only. fromContentState.updateValuesBeforeInterruption(toContentState) + return toContentState } + + return null } private fun Element.State.selfUpdateValuesBeforeInterruption() { diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt index ab2324a87d81..160326792f81 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt @@ -80,8 +80,8 @@ import kotlinx.coroutines.launch @Stable internal fun Modifier.multiPointerDraggable( orientation: Orientation, - startDragImmediately: (pointersInfo: PointersInfo) -> Boolean, - onDragStarted: (pointersInfo: PointersInfo, overSlop: Float) -> DragController, + startDragImmediately: (pointersDown: PointersInfo.PointersDown) -> Boolean, + onDragStarted: (pointersDown: PointersInfo.PointersDown, overSlop: Float) -> DragController, onFirstPointerDown: () -> Unit = {}, swipeDetector: SwipeDetector = DefaultSwipeDetector, dispatcher: NestedScrollDispatcher, @@ -99,8 +99,9 @@ internal fun Modifier.multiPointerDraggable( private data class MultiPointerDraggableElement( private val orientation: Orientation, - private val startDragImmediately: (pointersInfo: PointersInfo) -> Boolean, - private val onDragStarted: (pointersInfo: PointersInfo, overSlop: Float) -> DragController, + private val startDragImmediately: (pointersDown: PointersInfo.PointersDown) -> Boolean, + private val onDragStarted: + (pointersDown: PointersInfo.PointersDown, overSlop: Float) -> DragController, private val onFirstPointerDown: () -> Unit, private val swipeDetector: SwipeDetector, private val dispatcher: NestedScrollDispatcher, @@ -126,8 +127,8 @@ private data class MultiPointerDraggableElement( internal class MultiPointerDraggableNode( orientation: Orientation, - var startDragImmediately: (pointersInfo: PointersInfo) -> Boolean, - var onDragStarted: (pointersInfo: PointersInfo, overSlop: Float) -> DragController, + var startDragImmediately: (pointersDown: PointersInfo.PointersDown) -> Boolean, + var onDragStarted: (pointersDown: PointersInfo.PointersDown, overSlop: Float) -> DragController, var onFirstPointerDown: () -> Unit, swipeDetector: SwipeDetector = DefaultSwipeDetector, private val dispatcher: NestedScrollDispatcher, @@ -185,20 +186,27 @@ internal class MultiPointerDraggableNode( private var lastPointerEvent: PointerEvent? = null private var startedPosition: Offset? = null - private var pointersDown: Int = 0 + private var countPointersDown: Int = 0 internal fun pointersInfo(): PointersInfo? { - val startedPosition = startedPosition - val lastPointerEvent = lastPointerEvent - if (startedPosition == null || lastPointerEvent == null) { - // This may be null, i.e. when the user uses TalkBack - return null - } + // This may be null, i.e. when the user uses TalkBack + val lastPointerEvent = lastPointerEvent ?: return null + + if (lastPointerEvent.type == PointerEventType.Scroll) return PointersInfo.MouseWheel - return PointersInfo( + val startedPosition = startedPosition ?: return null + + return PointersInfo.PointersDown( startedPosition = startedPosition, - pointersDown = pointersDown, - lastPointerEvent = lastPointerEvent, + count = countPointersDown, + countByType = + buildMap { + lastPointerEvent.changes.fastForEach { change -> + if (!change.pressed) return@fastForEach + val newValue = (get(change.type) ?: 0) + 1 + put(change.type, newValue) + } + }, ) } @@ -218,11 +226,11 @@ internal class MultiPointerDraggableNode( val changes = pointerEvent.changes lastPointerEvent = pointerEvent - pointersDown = changes.countDown() + countPointersDown = changes.countDown() when { // There are no more pointers down. - pointersDown == 0 -> { + countPointersDown == 0 -> { startedPosition = null // In case of multiple events with 0 pointers down (not pressed) we may have @@ -290,8 +298,8 @@ internal class MultiPointerDraggableNode( detectDragGestures( orientation = orientation, startDragImmediately = startDragImmediately, - onDragStart = { pointersInfo, overSlop -> - onDragStarted(pointersInfo, overSlop) + onDragStart = { pointersDown, overSlop -> + onDragStarted(pointersDown, overSlop) }, onDrag = { controller, amount -> dispatchScrollEvents( @@ -440,8 +448,8 @@ internal class MultiPointerDraggableNode( */ private suspend fun AwaitPointerEventScope.detectDragGestures( orientation: Orientation, - startDragImmediately: (pointersInfo: PointersInfo) -> Boolean, - onDragStart: (pointersInfo: PointersInfo, overSlop: Float) -> DragController, + startDragImmediately: (pointersDown: PointersInfo.PointersDown) -> Boolean, + onDragStart: (pointersDown: PointersInfo.PointersDown, overSlop: Float) -> DragController, onDrag: (controller: DragController, dragAmount: Float) -> Unit, onDragEnd: (controller: DragController) -> Unit, onDragCancel: (controller: DragController) -> Unit, @@ -466,13 +474,14 @@ internal class MultiPointerDraggableNode( .first() var overSlop = 0f - var lastPointersInfo = + var lastPointersDown: PointersInfo.PointersDown = checkNotNull(pointersInfo()) { "We should have pointers down, last event: $currentEvent" } + as PointersInfo.PointersDown val drag = - if (startDragImmediately(lastPointersInfo)) { + if (startDragImmediately(lastPointersDown)) { consumablePointer.consume() consumablePointer } else { @@ -499,10 +508,11 @@ internal class MultiPointerDraggableNode( ) } ?: return - lastPointersInfo = + lastPointersDown = checkNotNull(pointersInfo()) { "We should have pointers down, last event: $currentEvent" } + as PointersInfo.PointersDown // Make sure that overSlop is not 0f. This can happen when the user drags by exactly // the touch slop. However, the overSlop we pass to onDragStarted() is used to // compute the direction we are dragging in, so overSlop should never be 0f unless @@ -516,7 +526,7 @@ internal class MultiPointerDraggableNode( drag } - val controller = onDragStart(lastPointersInfo, overSlop) + val controller = onDragStart(lastPointersDown, overSlop) val successful: Boolean try { @@ -666,48 +676,31 @@ internal fun interface PointersInfoOwner { fun pointersInfo(): PointersInfo? } -/** - * Holds information about pointer interactions within a composable. - * - * This class stores details such as the starting position of a gesture, the number of pointers - * down, and whether the last pointer event was a mouse wheel scroll. - * - * @param startedPosition The starting position of the gesture. This is the position where the first - * pointer touched the screen, not necessarily the point where dragging begins. This may be - * different from the initial touch position if a child composable intercepts the gesture before - * this one. - * @param pointersDown The number of pointers currently down. - * @param isMouseWheel Indicates whether the last pointer event was a mouse wheel scroll. - * @param pointersDownByType Provide a map of pointer types to the count of pointers of that type - * currently down/pressed. - */ -internal data class PointersInfo( - val startedPosition: Offset, - val pointersDown: Int, - val isMouseWheel: Boolean, - val pointersDownByType: Map<PointerType, Int>, -) { - init { - check(pointersDown > 0) { "We should have at least 1 pointer down, $pointersDown instead" } +internal sealed interface PointersInfo { + /** + * Holds information about pointer interactions within a composable. + * + * This class stores details such as the starting position of a gesture, the number of pointers + * down, and whether the last pointer event was a mouse wheel scroll. + * + * @param startedPosition The starting position of the gesture. This is the position where the + * first pointer touched the screen, not necessarily the point where dragging begins. This may + * be different from the initial touch position if a child composable intercepts the gesture + * before this one. + * @param count The number of pointers currently down. + * @param countByType Provide a map of pointer types to the count of pointers of that type + * currently down/pressed. + */ + data class PointersDown( + val startedPosition: Offset, + val count: Int, + val countByType: Map<PointerType, Int>, + ) : PointersInfo { + init { + check(count > 0) { "We should have at least 1 pointer down, $count instead" } + } } -} -private fun PointersInfo( - startedPosition: Offset, - pointersDown: Int, - lastPointerEvent: PointerEvent, -): PointersInfo { - return PointersInfo( - startedPosition = startedPosition, - pointersDown = pointersDown, - isMouseWheel = lastPointerEvent.type == PointerEventType.Scroll, - pointersDownByType = - buildMap { - lastPointerEvent.changes.fastForEach { change -> - if (!change.pressed) return@fastForEach - val newValue = (get(change.type) ?: 0) + 1 - put(change.type, newValue) - } - }, - ) + /** Indicates whether the last pointer event was a mouse wheel scroll. */ + data object MouseWheel : PointersInfo } diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt index 61332b61ed1b..72b29ee8848a 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt @@ -25,17 +25,13 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.compose.ui.util.fastAll import androidx.compose.ui.util.fastAny -import androidx.compose.ui.util.fastFilter import androidx.compose.ui.util.fastForEach import com.android.compose.animation.scene.content.state.TransitionState import com.android.compose.animation.scene.transformation.SharedElementTransformation -import com.android.compose.animation.scene.transition.link.LinkedTransition -import com.android.compose.animation.scene.transition.link.StateLink import kotlin.math.absoluteValue import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineStart import kotlinx.coroutines.Job -import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.launch /** @@ -236,7 +232,6 @@ fun MutableSceneTransitionLayoutState( canShowOverlay: (OverlayKey) -> Boolean = { true }, canHideOverlay: (OverlayKey) -> Boolean = { true }, canReplaceOverlay: (from: OverlayKey, to: OverlayKey) -> Boolean = { _, _ -> true }, - stateLinks: List<StateLink> = emptyList(), ): MutableSceneTransitionLayoutState { return MutableSceneTransitionLayoutStateImpl( initialScene, @@ -246,7 +241,6 @@ fun MutableSceneTransitionLayoutState( canShowOverlay, canHideOverlay, canReplaceOverlay, - stateLinks, ) } @@ -258,10 +252,7 @@ internal class MutableSceneTransitionLayoutStateImpl( internal val canChangeScene: (SceneKey) -> Boolean = { true }, internal val canShowOverlay: (OverlayKey) -> Boolean = { true }, internal val canHideOverlay: (OverlayKey) -> Boolean = { true }, - internal val canReplaceOverlay: (from: OverlayKey, to: OverlayKey) -> Boolean = { _, _ -> - true - }, - private val stateLinks: List<StateLink> = emptyList(), + internal val canReplaceOverlay: (from: OverlayKey, to: OverlayKey) -> Boolean = { _, _ -> true }, ) : MutableSceneTransitionLayoutState { private val creationThread: Thread = Thread.currentThread() @@ -364,20 +355,11 @@ internal class MutableSceneTransitionLayoutStateImpl( checkThread() try { - // Keep a reference to the previous transition (if any). - val previousTransition = currentTransition - // Start the transition. startTransitionInternal(transition, chain) - // Handle transition links. - previousTransition?.let { cancelActiveTransitionLinks(it) } - if (stateLinks.isNotEmpty()) { - coroutineScope { setupTransitionLinks(transition) } - } - // Run the transition until it is finished. - transition.run() + transition.runInternal() } finally { finishTransition(transition) } @@ -471,42 +453,6 @@ internal class MutableSceneTransitionLayoutStateImpl( ) } - private fun cancelActiveTransitionLinks(transition: TransitionState.Transition) { - transition.activeTransitionLinks.forEach { (link, linkedTransition) -> - link.target.finishTransition(linkedTransition) - } - transition.activeTransitionLinks.clear() - } - - private fun CoroutineScope.setupTransitionLinks(transition: TransitionState.Transition) { - stateLinks.fastForEach { stateLink -> - val matchingLinks = - stateLink.transitionLinks.fastFilter { it.isMatchingLink(transition) } - if (matchingLinks.isEmpty()) return@fastForEach - if (matchingLinks.size > 1) error("More than one link matched.") - - val targetCurrentScene = stateLink.target.transitionState.currentScene - val matchingLink = matchingLinks[0] - - if (!matchingLink.targetIsInValidState(targetCurrentScene)) return@fastForEach - - val linkedTransition = - LinkedTransition( - originalTransition = transition, - fromScene = targetCurrentScene, - toScene = matchingLink.targetTo, - key = matchingLink.targetTransitionKey, - ) - - // Start with UNDISPATCHED so that startTransition is called directly and the new linked - // transition is observable directly. - launch(start = CoroutineStart.UNDISPATCHED) { - stateLink.target.startTransition(linkedTransition) - } - transition.activeTransitionLinks[stateLink] = linkedTransition - } - } - /** * Notify that [transition] was finished and that it settled to its * [currentScene][TransitionState.currentScene]. This will do nothing if [transition] was @@ -535,9 +481,6 @@ internal class MutableSceneTransitionLayoutStateImpl( // Mark this transition as finished. finishedTransitions.add(transition) - // Finish all linked transitions. - finishActiveTransitionLinks(transition) - // Keep a reference to the last transition, in case we remove all transitions and should // settle to Idle. val lastTransition = transitionStates.last() @@ -584,13 +527,6 @@ internal class MutableSceneTransitionLayoutStateImpl( transitionStates = listOf(TransitionState.Idle(scene, currentOverlays)) } - private fun finishActiveTransitionLinks(transition: TransitionState.Transition) { - for ((link, linkedTransition) in transition.activeTransitionLinks) { - link.target.finishTransition(linkedTransition) - } - transition.activeTransitionLinks.clear() - } - /** * Check if a transition is in progress. If the progress value is near 0 or 1, immediately snap * to the closest scene. diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt index ba5f4144aff9..a448ee49d944 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt @@ -56,7 +56,7 @@ private fun DraggableHandlerImpl.contentForSwipes(): Content { } /** Whether swipe should be enabled in the given [orientation]. */ -private fun Content.shouldEnableSwipes(orientation: Orientation): Boolean { +internal fun Content.shouldEnableSwipes(orientation: Orientation): Boolean { if (userActions.isEmpty()) { return false } @@ -200,10 +200,10 @@ private class SwipeToSceneNode( override fun onCancelPointerInput() = multiPointerDraggableNode.onCancelPointerInput() - private fun startDragImmediately(pointersInfo: PointersInfo): Boolean { + private fun startDragImmediately(pointersDown: PointersInfo.PointersDown): Boolean { // Immediately start the drag if the user can't swipe in the other direction and the gesture // handler can intercept it. - return !canOppositeSwipe() && draggableHandler.shouldImmediatelyIntercept(pointersInfo) + return !canOppositeSwipe() && draggableHandler.shouldImmediatelyIntercept(pointersDown) } private fun canOppositeSwipe(): Boolean { diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/TransitionState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/TransitionState.kt index 7d29a68e3a8d..e3118d67b434 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/TransitionState.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/state/TransitionState.kt @@ -18,6 +18,7 @@ package com.android.compose.animation.scene.content.state import androidx.compose.animation.core.Animatable import androidx.compose.animation.core.AnimationVector1D +import androidx.compose.animation.core.Spring import androidx.compose.animation.core.spring import androidx.compose.foundation.gestures.Orientation import androidx.compose.runtime.Stable @@ -34,8 +35,6 @@ import com.android.compose.animation.scene.SceneTransitionLayoutImpl import com.android.compose.animation.scene.TransformationSpec import com.android.compose.animation.scene.TransformationSpecImpl import com.android.compose.animation.scene.TransitionKey -import com.android.compose.animation.scene.transition.link.LinkedTransition -import com.android.compose.animation.scene.transition.link.StateLink import kotlinx.coroutines.launch /** The state associated to a [SceneTransitionLayout] at some specific point in time. */ @@ -280,8 +279,8 @@ sealed interface TransitionState { */ private var interruptionDecay: Animatable<Float, AnimationVector1D>? = null - /** The map of active links that connects this transition to other transitions. */ - internal val activeTransitionLinks = mutableMapOf<StateLink, LinkedTransition>() + /** Whether this transition was already started. */ + private var wasStarted = false init { check(fromContent != toContent) @@ -328,7 +327,7 @@ sealed interface TransitionState { } /** Run this transition and return once it is finished. */ - abstract suspend fun run() + protected abstract suspend fun run() /** * Freeze this transition state so that neither [currentScene] nor [currentOverlays] will @@ -341,6 +340,13 @@ sealed interface TransitionState { */ abstract fun freezeAndAnimateToCurrentState() + internal suspend fun runInternal() { + check(!wasStarted) { "A Transition can be started only once." } + wasStarted = true + + run() + } + internal fun updateOverscrollSpecs( fromSpec: OverscrollSpecImpl?, toSpec: OverscrollSpecImpl?, @@ -376,7 +382,7 @@ sealed interface TransitionState { val progressSpec = spring( stiffness = swipeSpec.stiffness, - dampingRatio = swipeSpec.dampingRatio, + dampingRatio = Spring.DampingRatioNoBouncy, visibilityThreshold = ProgressVisibilityThreshold, ) animatable.animateTo(0f, progressSpec) diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/link/LinkedTransition.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/link/LinkedTransition.kt deleted file mode 100644 index 42ba9ba95e07..000000000000 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/link/LinkedTransition.kt +++ /dev/null @@ -1,59 +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.compose.animation.scene.transition.link - -import com.android.compose.animation.scene.SceneKey -import com.android.compose.animation.scene.TransitionKey -import com.android.compose.animation.scene.content.state.TransitionState - -/** A linked transition which is driven by a [originalTransition]. */ -internal class LinkedTransition( - private val originalTransition: TransitionState.Transition, - fromScene: SceneKey, - toScene: SceneKey, - override val key: TransitionKey? = null, -) : TransitionState.Transition.ChangeScene(fromScene, toScene) { - - override val currentScene: SceneKey - get() { - return when (originalTransition.currentScene) { - originalTransition.fromContent -> fromScene - originalTransition.toContent -> toScene - else -> error("Original currentScene is neither FromScene nor ToScene") - } - } - - override val isInitiatedByUserInput: Boolean - get() = originalTransition.isInitiatedByUserInput - - override val isUserInputOngoing: Boolean - get() = originalTransition.isUserInputOngoing - - override val progress: Float - get() = originalTransition.progress - - override val progressVelocity: Float - get() = originalTransition.progressVelocity - - override suspend fun run() { - originalTransition.run() - } - - override fun freezeAndAnimateToCurrentState() { - originalTransition.freezeAndAnimateToCurrentState() - } -} diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/link/StateLink.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/link/StateLink.kt deleted file mode 100644 index 2aec5091c3e5..000000000000 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transition/link/StateLink.kt +++ /dev/null @@ -1,63 +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.compose.animation.scene.transition.link - -import com.android.compose.animation.scene.ContentKey -import com.android.compose.animation.scene.MutableSceneTransitionLayoutStateImpl -import com.android.compose.animation.scene.SceneKey -import com.android.compose.animation.scene.SceneTransitionLayoutState -import com.android.compose.animation.scene.TransitionKey -import com.android.compose.animation.scene.content.state.TransitionState - -/** A link between a source (implicit) and [target] `SceneTransitionLayoutState`. */ -class StateLink(target: SceneTransitionLayoutState, val transitionLinks: List<TransitionLink>) { - - internal val target = target as MutableSceneTransitionLayoutStateImpl - - /** - * Links two transitions (source and target) together. - * - * `null` can be passed to indicate that any SceneKey should match. e.g. passing `null`, `null`, - * `null`, `SceneA` means that any transition at the source will trigger a transition in the - * target to `SceneA` from any current scene. - */ - class TransitionLink( - val sourceFrom: ContentKey?, - val sourceTo: ContentKey?, - val targetFrom: SceneKey?, - val targetTo: SceneKey, - val targetTransitionKey: TransitionKey? = null, - ) { - init { - if ( - (sourceFrom != null && sourceFrom == sourceTo) || - (targetFrom != null && targetFrom == targetTo) - ) - error("From and To can't be the same") - } - - internal fun isMatchingLink(transition: TransitionState.Transition): Boolean { - return (sourceFrom == null || sourceFrom == transition.fromContent) && - (sourceTo == null || sourceTo == transition.toContent) - } - - internal fun targetIsInValidState(targetCurrentContent: ContentKey): Boolean { - return (targetFrom == null || targetFrom == targetCurrentContent) && - targetTo != targetCurrentContent - } - } -} diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt index 098673e5d186..7e6f3a88fab1 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt @@ -52,17 +52,15 @@ import org.junit.runner.RunWith private const val SCREEN_SIZE = 100f private val LAYOUT_SIZE = IntSize(SCREEN_SIZE.toInt(), SCREEN_SIZE.toInt()) -private fun pointersInfo( +private fun pointersDown( startedPosition: Offset = Offset.Zero, pointersDown: Int = 1, - isMouseWheel: Boolean = false, pointersDownByType: Map<PointerType, Int> = mapOf(PointerType.Touch to pointersDown), -): PointersInfo { - return PointersInfo( +): PointersInfo.PointersDown { + return PointersInfo.PointersDown( startedPosition = startedPosition, - pointersDown = pointersDown, - isMouseWheel = isMouseWheel, - pointersDownByType = pointersDownByType, + count = pointersDown, + countByType = pointersDownByType, ) } @@ -138,7 +136,7 @@ class DraggableHandlerTest { val draggableHandler = layoutImpl.draggableHandler(Orientation.Vertical) val horizontalDraggableHandler = layoutImpl.draggableHandler(Orientation.Horizontal) - var pointerInfoOwner: () -> PointersInfo = { pointersInfo() } + var pointerInfoOwner: () -> PointersInfo = { pointersDown() } fun nestedScrollConnection( nestedScrollBehavior: NestedScrollBehavior, @@ -221,7 +219,7 @@ class DraggableHandlerTest { } fun onDragStarted( - pointersInfo: PointersInfo = pointersInfo(), + pointersInfo: PointersInfo.PointersDown = pointersDown(), overSlop: Float, expectedConsumedOverSlop: Float = overSlop, ): DragController { @@ -235,18 +233,20 @@ class DraggableHandlerTest { ) } - fun onDragStartedImmediately(pointersInfo: PointersInfo = pointersInfo()): DragController { + fun onDragStartedImmediately( + pointersInfo: PointersInfo.PointersDown = pointersDown() + ): DragController { return onDragStarted(draggableHandler, pointersInfo, overSlop = 0f) } fun onDragStarted( draggableHandler: DraggableHandler, - pointersInfo: PointersInfo = pointersInfo(), + pointersInfo: PointersInfo.PointersDown = pointersDown(), overSlop: Float = 0f, expectedConsumedOverSlop: Float = overSlop, ): DragController { val dragController = - draggableHandler.onDragStarted(pointersInfo = pointersInfo, overSlop = overSlop) + draggableHandler.onDragStarted(pointersDown = pointersInfo, overSlop = overSlop) // MultiPointerDraggable will always call onDelta with the initial overSlop right after dragController.onDragDelta(pixels = overSlop, expectedConsumedOverSlop) @@ -529,7 +529,7 @@ class DraggableHandlerTest { val dragController = onDragStarted( pointersInfo = - pointersInfo(startedPosition = Offset(SCREEN_SIZE * 0.5f, SCREEN_SIZE * 0.5f)), + pointersDown(startedPosition = Offset(SCREEN_SIZE * 0.5f, SCREEN_SIZE * 0.5f)), overSlop = up(fractionOfScreen = 0.2f), ) assertTransition( @@ -555,7 +555,7 @@ class DraggableHandlerTest { // Start dragging from the bottom onDragStarted( - pointersInfo = pointersInfo(startedPosition = Offset(SCREEN_SIZE * 0.5f, SCREEN_SIZE)), + pointersInfo = pointersDown(startedPosition = Offset(SCREEN_SIZE * 0.5f, SCREEN_SIZE)), overSlop = up(fractionOfScreen = 0.1f), ) assertTransition( @@ -1052,7 +1052,7 @@ class DraggableHandlerTest { navigateToSceneC() // Swipe up from the middle to transition to scene B. - val middle = pointersInfo(startedPosition = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f)) + val middle = pointersDown(startedPosition = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f)) onDragStarted(pointersInfo = middle, overSlop = up(0.1f)) assertTransition( currentScene = SceneC, @@ -1084,7 +1084,7 @@ class DraggableHandlerTest { // Start a new gesture from the bottom of the screen. Because swiping up from the bottom of // C leads to scene A (and not B), the previous transitions is *not* intercepted and we // instead animate from C to A. - val bottom = pointersInfo(startedPosition = Offset(SCREEN_SIZE / 2, SCREEN_SIZE)) + val bottom = pointersDown(startedPosition = Offset(SCREEN_SIZE / 2, SCREEN_SIZE)) assertThat(draggableHandler.shouldImmediatelyIntercept(bottom)).isFalse() onDragStarted(pointersInfo = bottom, overSlop = up(0.1f)) @@ -1103,7 +1103,7 @@ class DraggableHandlerTest { navigateToSceneC() // Swipe up from the middle to transition to scene B. - val middle = pointersInfo(startedPosition = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f)) + val middle = pointersDown(startedPosition = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f)) onDragStarted(pointersInfo = middle, overSlop = up(0.1f)) assertTransition(fromScene = SceneC, toScene = SceneB, isUserInputOngoing = true) @@ -1120,15 +1120,15 @@ class DraggableHandlerTest { @Test fun interruptedTransitionCanNotBeImmediatelyIntercepted() = runGestureTest { - assertThat(draggableHandler.shouldImmediatelyIntercept(pointersInfo = null)).isFalse() + assertThat(draggableHandler.shouldImmediatelyIntercept(pointersDown = null)).isFalse() onDragStarted(overSlop = up(0.1f)) - assertThat(draggableHandler.shouldImmediatelyIntercept(pointersInfo = null)).isTrue() + assertThat(draggableHandler.shouldImmediatelyIntercept(pointersDown = null)).isTrue() layoutState.startTransitionImmediately( animationScope = testScope.backgroundScope, transition(SceneA, SceneB), ) - assertThat(draggableHandler.shouldImmediatelyIntercept(pointersInfo = null)).isFalse() + assertThat(draggableHandler.shouldImmediatelyIntercept(pointersDown = null)).isFalse() } @Test @@ -1160,7 +1160,7 @@ class DraggableHandlerTest { assertTransition(currentScene = SceneB, fromScene = SceneA, toScene = SceneB) // Intercept the transition and swipe down back to scene A. - assertThat(draggableHandler.shouldImmediatelyIntercept(pointersInfo = null)).isTrue() + assertThat(draggableHandler.shouldImmediatelyIntercept(pointersDown = null)).isTrue() val dragController2 = onDragStartedImmediately() // Block the transition when the user release their finger. @@ -1204,7 +1204,7 @@ class DraggableHandlerTest { val nestedScroll = nestedScrollConnection(nestedScrollBehavior = EdgeAlways) // Drag from the **top** of the screen - pointerInfoOwner = { pointersInfo() } + pointerInfoOwner = { pointersDown() } assertIdle(currentScene = SceneC) nestedScroll.scroll(available = upOffset(fractionOfScreen = 0.1f)) @@ -1221,7 +1221,7 @@ class DraggableHandlerTest { advanceUntilIdle() // Drag from the **bottom** of the screen - pointerInfoOwner = { pointersInfo(startedPosition = Offset(0f, SCREEN_SIZE)) } + pointerInfoOwner = { pointersDown(startedPosition = Offset(0f, SCREEN_SIZE)) } assertIdle(currentScene = SceneC) nestedScroll.scroll(available = upOffset(fractionOfScreen = 0.1f)) @@ -1241,7 +1241,7 @@ class DraggableHandlerTest { val nestedScroll = nestedScrollConnection(nestedScrollBehavior = EdgeAlways) // Use mouse wheel - pointerInfoOwner = { pointersInfo(isMouseWheel = true) } + pointerInfoOwner = { PointersInfo.MouseWheel } assertIdle(currentScene = SceneC) nestedScroll.scroll(available = upOffset(fractionOfScreen = 0.1f)) @@ -1251,7 +1251,7 @@ class DraggableHandlerTest { @Test fun transitionIsImmediatelyUpdatedWhenReleasingFinger() = runGestureTest { // Swipe up from the middle to transition to scene B. - val middle = pointersInfo(startedPosition = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f)) + val middle = pointersDown(startedPosition = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f)) val dragController = onDragStarted(pointersInfo = middle, overSlop = up(0.1f)) assertTransition(fromScene = SceneA, toScene = SceneB, isUserInputOngoing = true) @@ -1265,7 +1265,7 @@ class DraggableHandlerTest { layoutState.transitions = transitions { overscrollDisabled(SceneB, Orientation.Vertical) } // Swipe up to scene B at progress = 200%. - val middle = pointersInfo(startedPosition = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f)) + val middle = pointersDown(startedPosition = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f)) val dragController = onDragStarted( pointersInfo = middle, @@ -1296,7 +1296,7 @@ class DraggableHandlerTest { layoutState.transitions = transitions { overscrollDisabled(SceneB, Orientation.Vertical) } // Swipe up to scene B at progress = 200%. - val middle = pointersInfo(startedPosition = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f)) + val middle = pointersDown(startedPosition = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f)) val dragController = onDragStarted(pointersInfo = middle, overSlop = up(0.99f)) assertTransition(fromScene = SceneA, toScene = SceneB, progress = 0.99f) @@ -1342,7 +1342,7 @@ class DraggableHandlerTest { overscroll(SceneB, Orientation.Vertical) { fade(TestElements.Foo) } } - val middle = pointersInfo(startedPosition = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f)) + val middle = pointersDown(startedPosition = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f)) val dragController = onDragStarted(pointersInfo = middle, overSlop = up(0.5f)) val transition = assertThat(transitionState).isSceneTransition() @@ -1374,7 +1374,7 @@ class DraggableHandlerTest { overscroll(SceneC, Orientation.Vertical) { fade(TestElements.Foo) } } - val middle = pointersInfo(startedPosition = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f)) + val middle = pointersDown(startedPosition = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f)) val dragController = onDragStarted(pointersInfo = middle, overSlop = down(0.5f)) val transition = assertThat(transitionState).isSceneTransition() @@ -1405,7 +1405,7 @@ class DraggableHandlerTest { overscroll(SceneB, Orientation.Vertical) { fade(TestElements.Foo) } } - val middle = pointersInfo(startedPosition = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f)) + val middle = pointersDown(startedPosition = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f)) val dragController = onDragStarted(pointersInfo = middle, overSlop = up(1.5f)) val transition = assertThat(transitionState).isSceneTransition() @@ -1437,7 +1437,7 @@ class DraggableHandlerTest { overscroll(SceneC, Orientation.Vertical) { fade(TestElements.Foo) } } - val middle = pointersInfo(startedPosition = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f)) + val middle = pointersDown(startedPosition = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f)) val dragController = onDragStarted(pointersInfo = middle, overSlop = down(1.5f)) val transition = assertThat(transitionState).isSceneTransition() @@ -1471,7 +1471,7 @@ class DraggableHandlerTest { mutableUserActionsA = mapOf(Swipe.Up to UserActionResult(SceneB)) - val middle = pointersInfo(startedPosition = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f)) + val middle = pointersDown(startedPosition = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f)) val dragController = onDragStarted(pointersInfo = middle, overSlop = down(1f)) val transition = assertThat(transitionState).isSceneTransition() assertThat(transition).hasFromScene(SceneA) @@ -1504,7 +1504,7 @@ class DraggableHandlerTest { mutableUserActionsA = mapOf(Swipe.Down to UserActionResult(SceneC)) - val middle = pointersInfo(startedPosition = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f)) + val middle = pointersDown(startedPosition = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f)) val dragController = onDragStarted(pointersInfo = middle, overSlop = up(1f)) val transition = assertThat(transitionState).isSceneTransition() assertThat(transition).hasFromScene(SceneA) @@ -1676,4 +1676,33 @@ class DraggableHandlerTest { assertThat(layoutState.transitionState).hasCurrentScene(SceneA) assertThat(layoutState.transitionState).hasCurrentOverlays(OverlayB) } + + @Test + fun replaceOverlayNestedScroll() = runGestureTest { + layoutState.showOverlay(OverlayA, animationScope = testScope) + advanceUntilIdle() + + // Initial state. + assertThat(layoutState.transitionState).isIdle() + assertThat(layoutState.transitionState).hasCurrentScene(SceneA) + assertThat(layoutState.transitionState).hasCurrentOverlays(OverlayA) + + // Swipe down to replace overlay A by overlay B. + + val nestedScroll = nestedScrollConnection(nestedScrollBehavior = EdgeWithPreview) + nestedScroll.scroll(downOffset(0.1f)) + val transition = assertThat(layoutState.transitionState).isReplaceOverlayTransition() + assertThat(transition).hasCurrentScene(SceneA) + assertThat(transition).hasFromOverlay(OverlayA) + assertThat(transition).hasToOverlay(OverlayB) + assertThat(transition).hasCurrentOverlays(OverlayA) + assertThat(transition).hasProgress(0.1f) + + nestedScroll.preFling(Velocity(0f, velocityThreshold)) + advanceUntilIdle() + // Commit the gesture. The overlays are instantly swapped in the set of current overlays. + assertThat(layoutState.transitionState).isIdle() + assertThat(layoutState.transitionState).hasCurrentScene(SceneA) + assertThat(layoutState.transitionState).hasCurrentOverlays(OverlayB) + } } diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt index 4a9051598ddc..a301856d024f 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt @@ -2638,4 +2638,58 @@ class ElementTest { assertWithMessage("Frame $i didn't replace Foo").that(numberOfPlacements).isEqualTo(0) } } + + @Test + fun interruption_considerPreviousUniqueState() { + @Composable + fun SceneScope.Foo(modifier: Modifier = Modifier) { + Box(modifier.element(TestElements.Foo).size(50.dp)) + } + + val state = rule.runOnUiThread { MutableSceneTransitionLayoutState(SceneA) } + val scope = + rule.setContentAndCreateMainScope { + SceneTransitionLayout(state) { + scene(SceneA) { Box(Modifier.fillMaxSize()) { Foo() } } + scene(SceneB) { Box(Modifier.fillMaxSize()) } + scene(SceneC) { + Box(Modifier.fillMaxSize()) { Foo(Modifier.offset(x = 100.dp, y = 100.dp)) } + } + } + } + + // During A => B, Foo disappears and stays in its original position. + scope.launch { state.startTransition(transition(SceneA, SceneB)) } + rule + .onNode(isElement(TestElements.Foo)) + .assertSizeIsEqualTo(50.dp) + .assertPositionInRootIsEqualTo(0.dp, 0.dp) + + // Interrupt A => B by B => C. + var interruptionProgress by mutableFloatStateOf(1f) + scope.launch { + state.startTransition( + transition(SceneB, SceneC, interruptionProgress = { interruptionProgress }) + ) + } + + // During B => C, Foo appears again. It is still at (0, 0) when the interruption progress is + // 100%, and converges to its position (100, 100) in C. + rule + .onNode(isElement(TestElements.Foo)) + .assertSizeIsEqualTo(50.dp) + .assertPositionInRootIsEqualTo(0.dp, 0.dp) + + interruptionProgress = 0.5f + rule + .onNode(isElement(TestElements.Foo)) + .assertSizeIsEqualTo(50.dp) + .assertPositionInRootIsEqualTo(50.dp, 50.dp) + + interruptionProgress = 0f + rule + .onNode(isElement(TestElements.Foo)) + .assertSizeIsEqualTo(50.dp) + .assertPositionInRootIsEqualTo(100.dp, 100.dp) + } } diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt index a2b263b9f9ed..2b7090876bad 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutStateTest.kt @@ -26,10 +26,8 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import com.android.compose.animation.scene.TestScenes.SceneA import com.android.compose.animation.scene.TestScenes.SceneB import com.android.compose.animation.scene.TestScenes.SceneC -import com.android.compose.animation.scene.TestScenes.SceneD import com.android.compose.animation.scene.content.state.TransitionState import com.android.compose.animation.scene.subjects.assertThat -import com.android.compose.animation.scene.transition.link.StateLink import com.android.compose.animation.scene.transition.seekToScene import com.android.compose.test.MonotonicClockTestScope import com.android.compose.test.TestSceneTransition @@ -42,6 +40,7 @@ import kotlinx.coroutines.cancelAndJoin import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.consumeAsFlow import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.runTest import org.junit.Assert.assertThrows import org.junit.Rule @@ -132,147 +131,6 @@ class SceneTransitionLayoutStateTest { assertThat(state.transitionState).isEqualTo(TransitionState.Idle(SceneB)) } - private fun setupLinkedStates( - parentInitialScene: SceneKey = SceneC, - childInitialScene: SceneKey = SceneA, - sourceFrom: SceneKey? = SceneA, - sourceTo: SceneKey? = SceneB, - targetFrom: SceneKey? = SceneC, - targetTo: SceneKey = SceneD, - ): Pair<MutableSceneTransitionLayoutStateImpl, MutableSceneTransitionLayoutStateImpl> { - val parentState = MutableSceneTransitionLayoutState(parentInitialScene) - val link = - listOf( - StateLink( - parentState, - listOf(StateLink.TransitionLink(sourceFrom, sourceTo, targetFrom, targetTo)), - ) - ) - val childState = MutableSceneTransitionLayoutState(childInitialScene, stateLinks = link) - return Pair( - parentState as MutableSceneTransitionLayoutStateImpl, - childState as MutableSceneTransitionLayoutStateImpl, - ) - } - - @Test - fun linkedTransition_startsLinkAndFinishesLinkInToState() = runTest { - val (parentState, childState) = setupLinkedStates() - - val childTransition = transition(SceneA, SceneB) - - val job = - childState.startTransitionImmediately(animationScope = backgroundScope, childTransition) - assertThat(childState.isTransitioning(SceneA, SceneB)).isTrue() - assertThat(parentState.isTransitioning(SceneC, SceneD)).isTrue() - - childTransition.finish() - job.join() - assertThat(childState.transitionState).isEqualTo(TransitionState.Idle(SceneB)) - assertThat(parentState.transitionState).isEqualTo(TransitionState.Idle(SceneD)) - } - - @Test - fun linkedTransition_transitiveLink() = runTest { - val parentParentState = - MutableSceneTransitionLayoutState(SceneB) as MutableSceneTransitionLayoutStateImpl - val parentLink = - listOf( - StateLink( - parentParentState, - listOf(StateLink.TransitionLink(SceneC, SceneD, SceneB, SceneC)), - ) - ) - val parentState = - MutableSceneTransitionLayoutState(SceneC, stateLinks = parentLink) - as MutableSceneTransitionLayoutStateImpl - val link = - listOf( - StateLink( - parentState, - listOf(StateLink.TransitionLink(SceneA, SceneB, SceneC, SceneD)), - ) - ) - val childState = - MutableSceneTransitionLayoutState(SceneA, stateLinks = link) - as MutableSceneTransitionLayoutStateImpl - - val childTransition = transition(SceneA, SceneB) - - val job = - childState.startTransitionImmediately(animationScope = backgroundScope, childTransition) - assertThat(childState.isTransitioning(SceneA, SceneB)).isTrue() - assertThat(parentState.isTransitioning(SceneC, SceneD)).isTrue() - assertThat(parentParentState.isTransitioning(SceneB, SceneC)).isTrue() - - childTransition.finish() - job.join() - assertThat(childState.transitionState).isEqualTo(TransitionState.Idle(SceneB)) - assertThat(parentState.transitionState).isEqualTo(TransitionState.Idle(SceneD)) - assertThat(parentParentState.transitionState).isEqualTo(TransitionState.Idle(SceneC)) - } - - @Test - fun linkedTransition_linkProgressIsEqual() = runTest { - val (parentState, childState) = setupLinkedStates() - - var progress = 0f - val childTransition = transition(SceneA, SceneB, progress = { progress }) - - childState.startTransitionImmediately(animationScope = backgroundScope, childTransition) - assertThat(parentState.currentTransition?.progress).isEqualTo(0f) - - progress = .5f - assertThat(parentState.currentTransition?.progress).isEqualTo(.5f) - } - - @Test - fun linkedTransition_reverseTransitionIsNotLinked() = runTest { - val (parentState, childState) = setupLinkedStates() - - val childTransition = transition(SceneB, SceneA, current = { SceneB }) - - val job = - childState.startTransitionImmediately(animationScope = backgroundScope, childTransition) - assertThat(childState.isTransitioning(SceneB, SceneA)).isTrue() - assertThat(parentState.transitionState).isEqualTo(TransitionState.Idle(SceneC)) - - childTransition.finish() - job.join() - assertThat(childState.transitionState).isEqualTo(TransitionState.Idle(SceneB)) - assertThat(parentState.transitionState).isEqualTo(TransitionState.Idle(SceneC)) - } - - @Test - fun linkedTransition_startsLinkAndFinishesLinkInFromState() = runTest { - val (parentState, childState) = setupLinkedStates() - - val childTransition = transition(SceneA, SceneB, current = { SceneA }) - val job = - childState.startTransitionImmediately(animationScope = backgroundScope, childTransition) - - childTransition.finish() - job.join() - assertThat(childState.transitionState).isEqualTo(TransitionState.Idle(SceneA)) - assertThat(parentState.transitionState).isEqualTo(TransitionState.Idle(SceneC)) - } - - @Test - fun linkedTransition_startsLinkButLinkedStateIsTakenOver() = runTest { - val (parentState, childState) = setupLinkedStates() - - val childTransition = transition(SceneA, SceneB) - val parentTransition = transition(SceneC, SceneA) - val job = - childState.startTransitionImmediately(animationScope = backgroundScope, childTransition) - parentState.startTransitionImmediately(animationScope = backgroundScope, parentTransition) - - childTransition.finish() - job.join() - assertThat(childState.transitionState).isEqualTo(TransitionState.Idle(SceneB)) - assertThat(parentState.transitionState).isEqualTo(parentTransition) - } - @Test fun setTargetScene_withTransitionKey() = runMonotonicClockTest { val transitionkey = TransitionKey(debugName = "foo") @@ -393,51 +251,6 @@ class SceneTransitionLayoutStateTest { assertThat(state.isTransitioning()).isTrue() } - @Test - fun linkedTransition_fuzzyLinksAreMatchedAndStarted() = runTest { - val (parentState, childState) = setupLinkedStates(SceneC, SceneA, null, null, null, SceneD) - val childTransition = transition(SceneA, SceneB) - - val job = - childState.startTransitionImmediately(animationScope = backgroundScope, childTransition) - assertThat(childState.isTransitioning(SceneA, SceneB)).isTrue() - assertThat(parentState.isTransitioning(SceneC, SceneD)).isTrue() - - childTransition.finish() - job.join() - assertThat(childState.transitionState).isEqualTo(TransitionState.Idle(SceneB)) - assertThat(parentState.transitionState).isEqualTo(TransitionState.Idle(SceneD)) - } - - @Test - fun linkedTransition_fuzzyLinksAreMatchedAndResetToProperPreviousScene() = runTest { - val (parentState, childState) = - setupLinkedStates(SceneC, SceneA, SceneA, null, null, SceneD) - - val childTransition = transition(SceneA, SceneB, current = { SceneA }) - - val job = - childState.startTransitionImmediately(animationScope = backgroundScope, childTransition) - assertThat(childState.isTransitioning(SceneA, SceneB)).isTrue() - assertThat(parentState.isTransitioning(SceneC, SceneD)).isTrue() - - childTransition.finish() - job.join() - assertThat(childState.transitionState).isEqualTo(TransitionState.Idle(SceneA)) - assertThat(parentState.transitionState).isEqualTo(TransitionState.Idle(SceneC)) - } - - @Test - fun linkedTransition_fuzzyLinksAreNotMatched() = runTest { - val (parentState, childState) = - setupLinkedStates(SceneC, SceneA, SceneB, null, SceneC, SceneD) - val childTransition = transition(SceneA, SceneB) - - childState.startTransitionImmediately(animationScope = backgroundScope, childTransition) - assertThat(childState.isTransitioning(SceneA, SceneB)).isTrue() - assertThat(parentState.isTransitioning(SceneC, SceneD)).isFalse() - } - private fun MonotonicClockTestScope.startOverscrollableTransistionFromAtoB( progress: () -> Float, sceneTransitions: SceneTransitions, @@ -777,4 +590,15 @@ class SceneTransitionLayoutStateTest { assertThat(transition.progressTo(SceneA)).isEqualTo(1f - 0.2f) assertThrows(IllegalArgumentException::class.java) { transition.progressTo(SceneC) } } + + @Test + fun transitionCanBeStartedOnlyOnce() = runTest { + val state = MutableSceneTransitionLayoutState(SceneA) + val transition = transition(from = SceneA, to = SceneB) + + state.startTransitionImmediately(backgroundScope, transition) + assertThrows(IllegalStateException::class.java) { + runBlocking { state.startTransition(transition) } + } + } } diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt index 3b2ee98a2a93..97a96a4333cb 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt @@ -574,7 +574,7 @@ class SwipeToSceneTest { rule.setContent { touchSlop = LocalViewConfiguration.current.touchSlop SceneTransitionLayout(layoutState, Modifier.size(LayoutWidth, LayoutHeight)) { - scene(SceneA, userActions = mapOf(Swipe.Down to SceneB)) { + scene(SceneA, userActions = mapOf(Swipe.Up to SceneB, Swipe.Down to SceneB)) { Box( Modifier.fillMaxSize() // A scrollable that does not consume the scroll gesture diff --git a/packages/SystemUI/customization/res/values-sw600dp-land/dimens.xml b/packages/SystemUI/customization/res/values-sw600dp-land/dimens.xml new file mode 100644 index 000000000000..651e401681c6 --- /dev/null +++ b/packages/SystemUI/customization/res/values-sw600dp-land/dimens.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright (C) 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<resources> + <dimen name="keyguard_smartspace_top_offset">0dp</dimen> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-sw600dp/dimens.xml b/packages/SystemUI/customization/res/values-sw600dp/dimens.xml new file mode 100644 index 000000000000..10e630d44488 --- /dev/null +++ b/packages/SystemUI/customization/res/values-sw600dp/dimens.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright (C) 2024 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<resources> + <!-- For portrait direction in unfold foldable device, we don't need keyguard_smartspace_top_offset--> + <dimen name="keyguard_smartspace_top_offset">0dp</dimen> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/res/values/dimens.xml b/packages/SystemUI/customization/res/values/dimens.xml index c574d1fc674b..7feea6e5e8dd 100644 --- a/packages/SystemUI/customization/res/values/dimens.xml +++ b/packages/SystemUI/customization/res/values/dimens.xml @@ -33,4 +33,10 @@ <dimen name="small_clock_height">114dp</dimen> <dimen name="small_clock_padding_top">28dp</dimen> <dimen name="clock_padding_start">28dp</dimen> + + <!-- When large clock is showing, offset the smartspace by this amount --> + <dimen name="keyguard_smartspace_top_offset">12dp</dimen> + <!--Dimens used in both lockscreen preview and smartspace --> + <dimen name="date_weather_view_height">24dp</dimen> + <dimen name="enhanced_smartspace_height">104dp</dimen> </resources>
\ No newline at end of file diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt index a4782acaed9b..ee21ea6ee126 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt @@ -55,10 +55,7 @@ class FlexClockFaceController( override val view: View get() = layerController.view - override val config = - ClockFaceConfig( - hasCustomPositionUpdatedAnimation = false // TODO(b/364673982) - ) + override val config = ClockFaceConfig(hasCustomPositionUpdatedAnimation = true) override var theme = ThemeConfig(true, assets.seedColor) @@ -96,6 +93,19 @@ class FlexClockFaceController( layerController.view.layoutParams = lp } + /** See documentation at [FlexClockView.offsetGlyphsForStepClockAnimation]. */ + private fun offsetGlyphsForStepClockAnimation( + clockStartLeft: Int, + direction: Int, + fraction: Float + ) { + (view as? FlexClockView)?.offsetGlyphsForStepClockAnimation( + clockStartLeft, + direction, + fraction, + ) + } + override val layout: ClockFaceLayout = DefaultClockFaceLayout(view).apply { views[0].id = @@ -248,10 +258,12 @@ class FlexClockFaceController( override fun onPositionUpdated(fromLeft: Int, direction: Int, fraction: Float) { layerController.animations.onPositionUpdated(fromLeft, direction, fraction) + if (isLargeClock) offsetGlyphsForStepClockAnimation(fromLeft, direction, fraction) } override fun onPositionUpdated(distance: Float, fraction: Float) { layerController.animations.onPositionUpdated(distance, fraction) + // TODO(b/378128811) port stepping animation } } } diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt index d86c0d664590..593eba9d05cc 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt @@ -19,6 +19,7 @@ package com.android.systemui.shared.clocks.view import android.content.Context import android.graphics.Canvas import android.graphics.Point +import android.util.MathUtils.constrainedMap import android.view.View import android.view.ViewGroup import android.widget.RelativeLayout @@ -50,6 +51,8 @@ class FlexClockView(context: Context, val assets: AssetLoader, messageBuffer: Me ) } + private val digitOffsets = mutableMapOf<Int, Float>() + override fun addView(child: View?) { super.addView(child) (child as SimpleDigitalClockTextView).digitTranslateAnimator = @@ -76,7 +79,7 @@ class FlexClockView(context: Context, val assets: AssetLoader, messageBuffer: Me digitLeftTopMap[R.id.HOUR_SECOND_DIGIT] = Point(maxSingleDigitSize.x, 0) digitLeftTopMap[R.id.MINUTE_FIRST_DIGIT] = Point(0, maxSingleDigitSize.y) digitLeftTopMap[R.id.MINUTE_SECOND_DIGIT] = Point(maxSingleDigitSize) - digitLeftTopMap.forEach { _, point -> + digitLeftTopMap.forEach { (_, point) -> point.x += abs(aodTranslate.x) point.y += abs(aodTranslate.y) } @@ -89,11 +92,17 @@ class FlexClockView(context: Context, val assets: AssetLoader, messageBuffer: Me override fun onDraw(canvas: Canvas) { super.onDraw(canvas) - digitalClockTextViewMap.forEach { (id, _) -> - val textView = digitalClockTextViewMap[id]!! - canvas.translate(digitLeftTopMap[id]!!.x.toFloat(), digitLeftTopMap[id]!!.y.toFloat()) + digitalClockTextViewMap.forEach { (id, textView) -> + // save canvas location in anticipation of restoration later + canvas.save() + val xTranslateAmount = + digitOffsets.getOrDefault(id, 0f) + digitLeftTopMap[id]!!.x.toFloat() + // move canvas to location that the textView would like + canvas.translate(xTranslateAmount, digitLeftTopMap[id]!!.y.toFloat()) + // draw the textView at the location of the canvas above textView.draw(canvas) - canvas.translate(-digitLeftTopMap[id]!!.x.toFloat(), -digitLeftTopMap[id]!!.y.toFloat()) + // reset the canvas location back to 0 without drawing + canvas.restore() } } @@ -157,10 +166,108 @@ class FlexClockView(context: Context, val assets: AssetLoader, messageBuffer: Me } } + /** + * Offsets the textViews of the clock for the step clock animation. + * + * The animation makes the textViews of the clock move at different speeds, when the clock is + * moving horizontally. + * + * @param clockStartLeft the [getLeft] position of the clock, before it started moving. + * @param clockMoveDirection the direction in which it is moving. A positive number means right, + * and negative means left. + * @param moveFraction fraction of the clock movement. 0 means it is at the beginning, and 1 + * means it finished moving. + */ + fun offsetGlyphsForStepClockAnimation( + clockStartLeft: Int, + clockMoveDirection: Int, + moveFraction: Float, + ) { + val isMovingToCenter = if (isLayoutRtl) clockMoveDirection < 0 else clockMoveDirection > 0 + // The sign of moveAmountDeltaForDigit is already set here + // we can interpret (left - clockStartLeft) as (destinationPosition - originPosition) + // so we no longer need to multiply direct sign to moveAmountDeltaForDigit + val currentMoveAmount = left - clockStartLeft + for (i in 0 until NUM_DIGITS) { + val mapIndexToId = + when (i) { + 0 -> R.id.HOUR_FIRST_DIGIT + 1 -> R.id.HOUR_SECOND_DIGIT + 2 -> R.id.MINUTE_FIRST_DIGIT + 3 -> R.id.MINUTE_SECOND_DIGIT + else -> -1 + } + val digitFraction = + getDigitFraction( + digit = i, + isMovingToCenter = isMovingToCenter, + fraction = moveFraction, + ) + // left here is the final left position after the animation is done + val moveAmountForDigit = currentMoveAmount * digitFraction + var moveAmountDeltaForDigit = moveAmountForDigit - currentMoveAmount + if (isMovingToCenter && moveAmountForDigit < 0) moveAmountDeltaForDigit *= -1 + digitOffsets[mapIndexToId] = moveAmountDeltaForDigit + invalidate() + } + } + + private val moveToCenterDelays: List<Int> + get() = if (isLayoutRtl) MOVE_LEFT_DELAYS else MOVE_RIGHT_DELAYS + + private val moveToSideDelays: List<Int> + get() = if (isLayoutRtl) MOVE_RIGHT_DELAYS else MOVE_LEFT_DELAYS + + private fun getDigitFraction(digit: Int, isMovingToCenter: Boolean, fraction: Float): Float { + // The delay for the digit, in terms of fraction. + // (i.e. the digit should not move during 0.0 - 0.1). + val delays = if (isMovingToCenter) moveToCenterDelays else moveToSideDelays + val digitInitialDelay = delays[digit] * MOVE_DIGIT_STEP + return MOVE_INTERPOLATOR.getInterpolation( + constrainedMap( + /* rangeMin= */ 0.0f, + /* rangeMax= */ 1.0f, + /* valueMin= */ digitInitialDelay, + /* valueMax= */ digitInitialDelay + AVAILABLE_ANIMATION_TIME, + /* value= */ fraction, + ) + ) + } + companion object { val AOD_TRANSITION_DURATION = 750L val CHARGING_TRANSITION_DURATION = 300L + // Calculate the positions of all of the digits... + // Offset each digit by, say, 0.1 + // This means that each digit needs to move over a slice of "fractions", i.e. digit 0 should + // move from 0.0 - 0.7, digit 1 from 0.1 - 0.8, digit 2 from 0.2 - 0.9, and digit 3 + // from 0.3 - 1.0. + private const val NUM_DIGITS = 4 + + // Delays. Each digit's animation should have a slight delay, so we get a nice + // "stepping" effect. When moving right, the second digit of the hour should move first. + // When moving left, the first digit of the hour should move first. The lists encode + // the delay for each digit (hour[0], hour[1], minute[0], minute[1]), to be multiplied + // by delayMultiplier. + private val MOVE_LEFT_DELAYS = listOf(0, 1, 2, 3) + private val MOVE_RIGHT_DELAYS = listOf(1, 0, 3, 2) + + // How much delay to apply to each subsequent digit. This is measured in terms of "fraction" + // (i.e. a value of 0.1 would cause a digit to wait until fraction had hit 0.1, or 0.2 etc + // before moving). + // + // The current specs dictate that each digit should have a 33ms gap between them. The + // overall time is 1s right now. + private const val MOVE_DIGIT_STEP = 0.033f + + // Constants for the animation + private val MOVE_INTERPOLATOR = Interpolators.EMPHASIZED + + // Total available transition time for each digit, taking into account the step. If step is + // 0.1, then digit 0 would animate over 0.0 - 0.7, making availableTime 0.7. + private const val AVAILABLE_ANIMATION_TIME = 1.0f - MOVE_DIGIT_STEP * (NUM_DIGITS - 1) + // Use the sign of targetTranslation to control the direction of digit translation fun updateDirectionalTargetTranslate(id: Int, targetTranslation: Point): Point { val outPoint = Point(targetTranslation) @@ -169,17 +276,14 @@ class FlexClockView(context: Context, val assets: AssetLoader, messageBuffer: Me outPoint.x *= -1 outPoint.y *= -1 } - R.id.HOUR_SECOND_DIGIT -> { outPoint.x *= 1 outPoint.y *= -1 } - R.id.MINUTE_FIRST_DIGIT -> { outPoint.x *= -1 outPoint.y *= 1 } - R.id.MINUTE_SECOND_DIGIT -> { outPoint.x *= 1 outPoint.y *= 1 diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardDisplayManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardDisplayManagerTest.java deleted file mode 100644 index dd58ea7db2bc..000000000000 --- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardDisplayManagerTest.java +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.keyguard; - -import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.hardware.display.DisplayManagerGlobal; -import android.testing.TestableLooper; -import android.view.Display; -import android.view.DisplayInfo; - -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.filters.SmallTest; - -import com.android.systemui.SysuiTestCase; -import com.android.systemui.navigationbar.NavigationBarController; -import com.android.systemui.settings.FakeDisplayTracker; -import com.android.systemui.statusbar.policy.KeyguardStateController; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.util.concurrent.Executor; - -@SmallTest -@RunWith(AndroidJUnit4.class) -@TestableLooper.RunWithLooper -public class KeyguardDisplayManagerTest extends SysuiTestCase { - - @Mock - private NavigationBarController mNavigationBarController; - @Mock - private ConnectedDisplayKeyguardPresentation.Factory - mConnectedDisplayKeyguardPresentationFactory; - @Mock - private ConnectedDisplayKeyguardPresentation mConnectedDisplayKeyguardPresentation; - @Mock - private KeyguardDisplayManager.DeviceStateHelper mDeviceStateHelper; - @Mock - private KeyguardStateController mKeyguardStateController; - - private Executor mMainExecutor = Runnable::run; - private Executor mBackgroundExecutor = Runnable::run; - private KeyguardDisplayManager mManager; - private FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext); - // The default and secondary displays are both in the default group - private Display mDefaultDisplay; - private Display mSecondaryDisplay; - - // This display is in a different group from the default and secondary displays. - private Display mAlwaysUnlockedDisplay; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - mManager = spy(new KeyguardDisplayManager(mContext, () -> mNavigationBarController, - mDisplayTracker, mMainExecutor, mBackgroundExecutor, mDeviceStateHelper, - mKeyguardStateController, mConnectedDisplayKeyguardPresentationFactory)); - doReturn(mConnectedDisplayKeyguardPresentation).when( - mConnectedDisplayKeyguardPresentationFactory).create(any()); - doReturn(mConnectedDisplayKeyguardPresentation).when(mManager) - .createPresentation(any()); - mDefaultDisplay = new Display(DisplayManagerGlobal.getInstance(), Display.DEFAULT_DISPLAY, - new DisplayInfo(), DEFAULT_DISPLAY_ADJUSTMENTS); - mSecondaryDisplay = new Display(DisplayManagerGlobal.getInstance(), - Display.DEFAULT_DISPLAY + 1, - new DisplayInfo(), DEFAULT_DISPLAY_ADJUSTMENTS); - - DisplayInfo alwaysUnlockedDisplayInfo = new DisplayInfo(); - alwaysUnlockedDisplayInfo.displayId = Display.DEFAULT_DISPLAY + 2; - alwaysUnlockedDisplayInfo.flags = Display.FLAG_ALWAYS_UNLOCKED; - mAlwaysUnlockedDisplay = new Display(DisplayManagerGlobal.getInstance(), - Display.DEFAULT_DISPLAY, - alwaysUnlockedDisplayInfo, DEFAULT_DISPLAY_ADJUSTMENTS); - } - - @Test - public void testShow_defaultDisplayOnly() { - mDisplayTracker.setAllDisplays(new Display[]{mDefaultDisplay}); - mManager.show(); - verify(mManager, never()).createPresentation(any()); - } - - @Test - public void testShow_includeSecondaryDisplay() { - mDisplayTracker.setAllDisplays(new Display[]{mDefaultDisplay, mSecondaryDisplay}); - mManager.show(); - verify(mManager, times(1)).createPresentation(eq(mSecondaryDisplay)); - } - - @Test - public void testShow_includeAlwaysUnlockedDisplay() { - mDisplayTracker.setAllDisplays(new Display[]{mDefaultDisplay, mAlwaysUnlockedDisplay}); - - mManager.show(); - verify(mManager, never()).createPresentation(any()); - } - - @Test - public void testShow_includeSecondaryAndAlwaysUnlockedDisplays() { - mDisplayTracker.setAllDisplays( - new Display[]{mDefaultDisplay, mSecondaryDisplay, mAlwaysUnlockedDisplay}); - - mManager.show(); - verify(mManager, times(1)).createPresentation(eq(mSecondaryDisplay)); - } - - @Test - public void testShow_concurrentDisplayActive_occluded() { - mDisplayTracker.setAllDisplays(new Display[]{mDefaultDisplay, mSecondaryDisplay}); - - when(mDeviceStateHelper.isConcurrentDisplayActive(mSecondaryDisplay)).thenReturn(true); - when(mKeyguardStateController.isOccluded()).thenReturn(true); - verify(mManager, never()).createPresentation(eq(mSecondaryDisplay)); - } - - @Test - public void testShow_presentationCreated() { - when(mManager.createPresentation(any())).thenCallRealMethod(); - mDisplayTracker.setAllDisplays(new Display[]{mDefaultDisplay, mSecondaryDisplay}); - - mManager.show(); - - verify(mConnectedDisplayKeyguardPresentationFactory).create(eq(mSecondaryDisplay)); - } -} diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardDisplayManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardDisplayManagerTest.kt new file mode 100644 index 000000000000..57a67973f34f --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardDisplayManagerTest.kt @@ -0,0 +1,225 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.keyguard + +import android.hardware.display.DisplayManagerGlobal +import android.platform.test.annotations.EnableFlags +import android.testing.TestableLooper.RunWithLooper +import android.view.Display +import android.view.DisplayAdjustments +import android.view.DisplayInfo +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.keyguard.KeyguardDisplayManager.DeviceStateHelper +import com.android.systemui.Flags +import com.android.systemui.SysuiTestCase +import com.android.systemui.navigationbar.NavigationBarController +import com.android.systemui.settings.FakeDisplayTracker +import com.android.systemui.shade.data.repository.FakeShadePositionRepository +import com.android.systemui.statusbar.policy.KeyguardStateController +import java.util.concurrent.Executor +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.UnconfinedTestDispatcher +import kotlinx.coroutines.test.advanceUntilIdle +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito.mock +import org.mockito.Mockito.never +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations +import org.mockito.kotlin.any +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.eq +import org.mockito.kotlin.reset +import org.mockito.kotlin.whenever + +@SmallTest +@RunWith(AndroidJUnit4::class) +@RunWithLooper +@OptIn(ExperimentalCoroutinesApi::class) +class KeyguardDisplayManagerTest : SysuiTestCase() { + @Mock private val navigationBarController = mock(NavigationBarController::class.java) + @Mock + private val presentationFactory = mock(ConnectedDisplayKeyguardPresentation.Factory::class.java) + @Mock + private val connectedDisplayKeyguardPresentation = + mock(ConnectedDisplayKeyguardPresentation::class.java) + @Mock private val deviceStateHelper = mock(DeviceStateHelper::class.java) + @Mock private val keyguardStateController = mock(KeyguardStateController::class.java) + private val shadePositionRepository = FakeShadePositionRepository() + + private val mainExecutor = Executor { it.run() } + private val backgroundExecutor = Executor { it.run() } + private lateinit var manager: KeyguardDisplayManager + private val displayTracker = FakeDisplayTracker(mContext) + // The default and secondary displays are both in the default group + private lateinit var defaultDisplay: Display + private lateinit var secondaryDisplay: Display + + private val testScope = TestScope(UnconfinedTestDispatcher()) + + // This display is in a different group from the default and secondary displays. + private lateinit var alwaysUnlockedDisplay: Display + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + manager = + KeyguardDisplayManager( + mContext, + { navigationBarController }, + displayTracker, + mainExecutor, + backgroundExecutor, + deviceStateHelper, + keyguardStateController, + presentationFactory, + { shadePositionRepository }, + testScope.backgroundScope, + ) + whenever(presentationFactory.create(any())).doReturn(connectedDisplayKeyguardPresentation) + + defaultDisplay = + Display( + DisplayManagerGlobal.getInstance(), + Display.DEFAULT_DISPLAY, + DisplayInfo(), + DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS, + ) + secondaryDisplay = + Display( + DisplayManagerGlobal.getInstance(), + Display.DEFAULT_DISPLAY + 1, + DisplayInfo(), + DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS, + ) + + val alwaysUnlockedDisplayInfo = DisplayInfo() + alwaysUnlockedDisplayInfo.displayId = Display.DEFAULT_DISPLAY + 2 + alwaysUnlockedDisplayInfo.flags = Display.FLAG_ALWAYS_UNLOCKED + alwaysUnlockedDisplay = + Display( + DisplayManagerGlobal.getInstance(), + Display.DEFAULT_DISPLAY, + alwaysUnlockedDisplayInfo, + DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS, + ) + } + + @Test + fun testShow_defaultDisplayOnly() { + displayTracker.allDisplays = arrayOf(defaultDisplay) + manager.show() + verify(presentationFactory, never()).create(any()) + } + + @Test + fun testShow_includeSecondaryDisplay() { + displayTracker.allDisplays = arrayOf(defaultDisplay, secondaryDisplay) + manager.show() + verify(presentationFactory).create(eq(secondaryDisplay)) + } + + @Test + fun testShow_includeAlwaysUnlockedDisplay() { + displayTracker.allDisplays = arrayOf(defaultDisplay, alwaysUnlockedDisplay) + + manager.show() + verify(presentationFactory, never()).create(any()) + } + + @Test + fun testShow_includeSecondaryAndAlwaysUnlockedDisplays() { + displayTracker.allDisplays = + arrayOf(defaultDisplay, secondaryDisplay, alwaysUnlockedDisplay) + + manager.show() + verify(presentationFactory).create(eq(secondaryDisplay)) + } + + @Test + fun testShow_concurrentDisplayActive_occluded() { + displayTracker.allDisplays = arrayOf(defaultDisplay, secondaryDisplay) + + whenever(deviceStateHelper.isConcurrentDisplayActive(secondaryDisplay)).thenReturn(true) + whenever(keyguardStateController.isOccluded).thenReturn(true) + verify(presentationFactory, never()).create(eq(secondaryDisplay)) + } + + @Test + fun testShow_presentationCreated() { + displayTracker.allDisplays = arrayOf(defaultDisplay, secondaryDisplay) + + manager.show() + + verify(presentationFactory).create(eq(secondaryDisplay)) + } + + @Test + @EnableFlags(Flags.FLAG_SHADE_WINDOW_GOES_AROUND) + fun show_shadeMovesDisplay_newPresentationCreated() { + displayTracker.allDisplays = arrayOf(defaultDisplay, secondaryDisplay) + // Shade in the default display, we expect the presentation to be in the secondary only + shadePositionRepository.setDisplayId(defaultDisplay.displayId) + + manager.show() + + verify(presentationFactory).create(eq(secondaryDisplay)) + verify(presentationFactory, never()).create(eq(defaultDisplay)) + reset(presentationFactory) + whenever(presentationFactory.create(any())).thenReturn(connectedDisplayKeyguardPresentation) + + // Let's move it to the secondary display. We expect it will be added in the default + // one. + shadePositionRepository.setDisplayId(secondaryDisplay.displayId) + testScope.advanceUntilIdle() + + verify(presentationFactory).create(eq(defaultDisplay)) + reset(presentationFactory) + whenever(presentationFactory.create(any())).thenReturn(connectedDisplayKeyguardPresentation) + + // Let's move it back! it should be re-created (it means it was removed before) + shadePositionRepository.setDisplayId(defaultDisplay.displayId) + testScope.advanceUntilIdle() + + verify(presentationFactory).create(eq(secondaryDisplay)) + } + + @Test + @EnableFlags(Flags.FLAG_SHADE_WINDOW_GOES_AROUND) + fun show_shadeInSecondaryDisplay_defaultOneHasPresentation() { + displayTracker.allDisplays = arrayOf(defaultDisplay, secondaryDisplay) + shadePositionRepository.setDisplayId(secondaryDisplay.displayId) + + manager.show() + + verify(presentationFactory).create(eq(defaultDisplay)) + } + + @Test + @EnableFlags(Flags.FLAG_SHADE_WINDOW_GOES_AROUND) + fun show_shadeInDefaultDisplay_secondaryOneHasPresentation() { + displayTracker.allDisplays = arrayOf(defaultDisplay, secondaryDisplay) + shadePositionRepository.setDisplayId(defaultDisplay.displayId) + + manager.show() + + verify(presentationFactory).create(eq(secondaryDisplay)) + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractorImplTest.kt index e1421691a92d..58fe2c9cbe57 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractorImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractorImplTest.kt @@ -36,6 +36,7 @@ import org.mockito.junit.MockitoJUnit private const val USER_ID = 22 private const val OWNER_ID = 10 +private const val PASSWORD_ID = 30 private const val OPERATION_ID = 100L private const val MAX_ATTEMPTS = 5 @@ -247,7 +248,11 @@ class CredentialInteractorImplTest : SysuiTestCase() { private fun pinRequest(credentialOwner: Int = USER_ID): BiometricPromptRequest.Credential.Pin = BiometricPromptRequest.Credential.Pin( promptInfo(), - BiometricUserInfo(userId = USER_ID, deviceCredentialOwnerId = credentialOwner), + BiometricUserInfo( + userId = USER_ID, + deviceCredentialOwnerId = credentialOwner, + userIdForPasswordEntry = PASSWORD_ID, + ), BiometricOperationInfo(OPERATION_ID), ) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt index c803097134de..d75c0138bcbf 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt @@ -36,6 +36,7 @@ import android.hardware.biometrics.PromptVerticalListContentView import android.hardware.face.FaceSensorPropertiesInternal import android.hardware.fingerprint.FingerprintSensorProperties import android.hardware.fingerprint.FingerprintSensorPropertiesInternal +import android.os.UserHandle import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import android.view.HapticFeedbackConstants @@ -97,13 +98,14 @@ import platform.test.runner.parameterized.ParameterizedAndroidJunit4 import platform.test.runner.parameterized.Parameters private const val USER_ID = 4 +private const val WORK_USER_ID = 100 private const val REQUEST_ID = 4L private const val CHALLENGE = 2L private const val DELAY = 1000L +private const val OP_PACKAGE_NAME_WITH_ACTIVITY_LOGO = "should.use.activiy.logo" private const val OP_PACKAGE_NAME_WITH_APP_LOGO = "biometric.testapp" -private const val OP_PACKAGE_NAME_NO_ICON = "biometric.testapp.noicon" +private const val OP_PACKAGE_NAME_NO_LOGO_INFO = "biometric.testapp.nologoinfo" private const val OP_PACKAGE_NAME_CAN_NOT_BE_FOUND = "can.not.be.found" -private const val OP_PACKAGE_NAME_WITH_ACTIVITY_LOGO = "should.use.activiy.logo" @OptIn(ExperimentalCoroutinesApi::class) @SmallTest @@ -120,13 +122,15 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa private val defaultLogoIconFromAppInfo = context.getDrawable(R.drawable.ic_android) private val defaultLogoIconFromActivityInfo = context.getDrawable(R.drawable.ic_add) + private val defaultLogoIconWithBadge = context.getDrawable(R.drawable.ic_alarm) private val logoResFromApp = R.drawable.ic_cake private val logoDrawableFromAppRes = context.getDrawable(logoResFromApp) private val logoBitmapFromApp = Bitmap.createBitmap(400, 400, Bitmap.Config.RGB_565) private val defaultLogoDescriptionFromAppInfo = "Test Android App" private val defaultLogoDescriptionFromActivityInfo = "Test Coke App" + private val defaultLogoDescriptionWithBadge = "Work app" private val logoDescriptionFromApp = "Test Cake App" - private val packageNameForLogoWithOverrides = "should.use.overridden.logo" + private val authInteractionProperties = AuthInteractionProperties() /** Prompt panel size padding */ @@ -173,55 +177,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa @Before fun setup() { - // Set up default logo info and app customized info - whenever(kosmos.packageManager.getApplicationInfo(eq(OP_PACKAGE_NAME_NO_ICON), anyInt())) - .thenReturn(applicationInfoNoIconOrDescription) - whenever( - kosmos.packageManager.getApplicationInfo( - eq(OP_PACKAGE_NAME_WITH_APP_LOGO), - anyInt(), - ) - ) - .thenReturn(applicationInfoWithIconAndDescription) - whenever( - kosmos.packageManager.getApplicationInfo( - eq(OP_PACKAGE_NAME_WITH_ACTIVITY_LOGO), - anyInt(), - ) - ) - .thenReturn(applicationInfoWithIconAndDescription) - whenever( - kosmos.packageManager.getApplicationInfo( - eq(OP_PACKAGE_NAME_CAN_NOT_BE_FOUND), - anyInt(), - ) - ) - .thenThrow(NameNotFoundException()) - - whenever(kosmos.packageManager.getActivityInfo(any(), anyInt())).thenReturn(activityInfo) - whenever(kosmos.iconProvider.getIcon(activityInfo)) - .thenReturn(defaultLogoIconFromActivityInfo) - whenever(activityInfo.loadLabel(kosmos.packageManager)) - .thenReturn(defaultLogoDescriptionFromActivityInfo) - - whenever(kosmos.packageManager.getApplicationIcon(applicationInfoWithIconAndDescription)) - .thenReturn(defaultLogoIconFromAppInfo) - whenever(kosmos.packageManager.getApplicationLabel(applicationInfoWithIconAndDescription)) - .thenReturn(defaultLogoDescriptionFromAppInfo) - whenever(kosmos.packageManager.getApplicationIcon(applicationInfoNoIconOrDescription)) - .thenReturn(null) - whenever(kosmos.packageManager.getApplicationLabel(applicationInfoNoIconOrDescription)) - .thenReturn("") - whenever(kosmos.packageManager.getUserBadgedIcon(any(), any())).then { it.getArgument(0) } - whenever(kosmos.packageManager.getUserBadgedLabel(any(), any())).then { it.getArgument(0) } - - context.setMockPackageManager(kosmos.packageManager) - overrideResource(logoResFromApp, logoDrawableFromAppRes) - overrideResource( - R.array.config_useActivityLogoForBiometricPrompt, - arrayOf(OP_PACKAGE_NAME_WITH_ACTIVITY_LOGO), - ) - + setupLogo() overrideResource(R.dimen.biometric_dialog_fingerprint_icon_width, mockFingerprintIconWidth) overrideResource( R.dimen.biometric_dialog_fingerprint_icon_height, @@ -264,6 +220,74 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa .build() } + private fun setupLogo() { + // Set up app customized logo + overrideResource(logoResFromApp, logoDrawableFromAppRes) + + // Set up when activity info should be used + overrideResource( + R.array.config_useActivityLogoForBiometricPrompt, + arrayOf(OP_PACKAGE_NAME_WITH_ACTIVITY_LOGO), + ) + whenever(kosmos.packageManager.getActivityInfo(any(), anyInt())).thenReturn(activityInfo) + whenever(kosmos.iconProvider.getIcon(activityInfo)) + .thenReturn(defaultLogoIconFromActivityInfo) + whenever(activityInfo.loadLabel(kosmos.packageManager)) + .thenReturn(defaultLogoDescriptionFromActivityInfo) + + // Set up when application info should be used for default logo + whenever( + kosmos.packageManager.getApplicationInfo( + eq(OP_PACKAGE_NAME_WITH_APP_LOGO), + anyInt(), + ) + ) + .thenReturn(applicationInfoWithIconAndDescription) + whenever( + kosmos.packageManager.getApplicationInfo( + eq(OP_PACKAGE_NAME_WITH_ACTIVITY_LOGO), + anyInt(), + ) + ) + .thenReturn(applicationInfoWithIconAndDescription) + whenever(kosmos.packageManager.getApplicationIcon(applicationInfoWithIconAndDescription)) + .thenReturn(defaultLogoIconFromAppInfo) + whenever(kosmos.packageManager.getApplicationLabel(applicationInfoWithIconAndDescription)) + .thenReturn(defaultLogoDescriptionFromAppInfo) + + // Set up when package name cannot but found + whenever( + kosmos.packageManager.getApplicationInfo( + eq(OP_PACKAGE_NAME_CAN_NOT_BE_FOUND), + anyInt(), + ) + ) + .thenThrow(NameNotFoundException()) + + // Set up when no default logo from application info + whenever( + kosmos.packageManager.getApplicationInfo(eq(OP_PACKAGE_NAME_NO_LOGO_INFO), anyInt()) + ) + .thenReturn(applicationInfoNoIconOrDescription) + whenever(kosmos.packageManager.getApplicationIcon(applicationInfoNoIconOrDescription)) + .thenReturn(null) + whenever(kosmos.packageManager.getApplicationLabel(applicationInfoNoIconOrDescription)) + .thenReturn("") + + // Set up work badge + whenever(kosmos.packageManager.getUserBadgedIcon(any(), eq(UserHandle.of(USER_ID)))).then { + it.getArgument(0) + } + whenever(kosmos.packageManager.getUserBadgedLabel(any(), eq(UserHandle.of(USER_ID)))).then { + it.getArgument(0) + } + whenever(kosmos.packageManager.getUserBadgedIcon(any(), eq(UserHandle.of(WORK_USER_ID)))) + .then { defaultLogoIconWithBadge } + whenever(kosmos.packageManager.getUserBadgedLabel(any(), eq(UserHandle.of(WORK_USER_ID)))) + .then { defaultLogoDescriptionWithBadge } + context.setMockPackageManager(kosmos.packageManager) + } + @Test fun start_idle_and_show_authenticating() = runGenericTest(doNotStart = true) { @@ -1520,6 +1544,16 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo) assertThat(logoInfo).isNotNull() assertThat(logoInfo!!.first).isNull() + assertThat(logoInfo!!.second).isEqualTo("") + } + + @Test + fun logo_defaultIsNull() = + runGenericTest(packageName = OP_PACKAGE_NAME_NO_LOGO_INFO) { + val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo) + assertThat(logoInfo).isNotNull() + assertThat(logoInfo!!.first).isNull() + assertThat(logoInfo!!.second).isEqualTo("") } @Test @@ -1527,32 +1561,39 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa runGenericTest(packageName = OP_PACKAGE_NAME_WITH_ACTIVITY_LOGO) { val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo) + assertThat(logoInfo).isNotNull() // 1. PM.getApplicationInfo(OP_PACKAGE_NAME_WITH_ACTIVITY_LOGO) is set to return // applicationInfoWithIconAndDescription with "defaultLogoIconFromAppInfo", // 2. iconProvider.getIcon(activityInfo) is set to return // "defaultLogoIconFromActivityInfo" // For the apps with OP_PACKAGE_NAME_WITH_ACTIVITY_LOGO, 2 should be called instead of 1 - assertThat(logoInfo).isNotNull() assertThat(logoInfo!!.first).isEqualTo(defaultLogoIconFromActivityInfo) + // 1. PM.getApplicationInfo(OP_PACKAGE_NAME_WITH_ACTIVITY_LOGO) is set to return + // applicationInfoWithIconAndDescription with "defaultLogoDescriptionFromAppInfo", + // 2. activityInfo.loadLabel() is set to return defaultLogoDescriptionFromActivityInfo + // For the apps with OP_PACKAGE_NAME_WITH_ACTIVITY_LOGO, 2 should be called instead of 1 + assertThat(logoInfo!!.second).isEqualTo(defaultLogoDescriptionFromActivityInfo) } @Test - fun logo_defaultIsNull() = - runGenericTest(packageName = OP_PACKAGE_NAME_NO_ICON) { - val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo) - assertThat(logoInfo).isNotNull() - assertThat(logoInfo!!.first).isNull() - } - - @Test - fun logo_default() = runGenericTest { + fun logo_defaultFromApplicationInfo() = runGenericTest { val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo) assertThat(logoInfo).isNotNull() assertThat(logoInfo!!.first).isEqualTo(defaultLogoIconFromAppInfo) + assertThat(logoInfo!!.second).isEqualTo(defaultLogoDescriptionFromAppInfo) } @Test - fun logo_resSetByApp() = + fun logo_defaultWithWorkBadge() = + runGenericTest(userId = WORK_USER_ID) { + val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo) + assertThat(logoInfo).isNotNull() + assertThat(logoInfo!!.first).isEqualTo(defaultLogoIconWithBadge) + assertThat(logoInfo!!.second).isEqualTo(defaultLogoDescriptionWithBadge) + } + + @Test + fun logoRes_setByApp() = runGenericTest(logoRes = logoResFromApp) { val expectedBitmap = context.getDrawable(logoResFromApp).toBitmap() val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo) @@ -1561,44 +1602,13 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa } @Test - fun logo_bitmapSetByApp() = + fun logoBitmap_setByApp() = runGenericTest(logoBitmap = logoBitmapFromApp) { val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo) assertThat((logoInfo!!.first as BitmapDrawable).bitmap).isEqualTo(logoBitmapFromApp) } @Test - fun logoDescription_emptyIfPkgNameNotFound() = - runGenericTest(packageName = OP_PACKAGE_NAME_CAN_NOT_BE_FOUND) { - val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo) - assertThat(logoInfo!!.second).isEqualTo("") - } - - @Test - fun logoDescription_defaultFromActivityInfo() = - runGenericTest(packageName = OP_PACKAGE_NAME_WITH_ACTIVITY_LOGO) { - val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo) - // 1. PM.getApplicationInfo(packageNameForLogoWithOverrides) is set to return - // applicationInfoWithIconAndDescription with defaultLogoDescription, - // 2. activityInfo.loadLabel() is set to return defaultLogoDescriptionWithOverrides - // For the apps with packageNameForLogoWithOverrides, 2 should be called instead of 1 - assertThat(logoInfo!!.second).isEqualTo(defaultLogoDescriptionFromActivityInfo) - } - - @Test - fun logoDescription_defaultIsEmpty() = - runGenericTest(packageName = OP_PACKAGE_NAME_NO_ICON) { - val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo) - assertThat(logoInfo!!.second).isEqualTo("") - } - - @Test - fun logoDescription_default() = runGenericTest { - val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo) - assertThat(logoInfo!!.second).isEqualTo(defaultLogoDescriptionFromAppInfo) - } - - @Test fun logoDescription_setByApp() = runGenericTest(logoDescription = logoDescriptionFromApp) { val logoInfo by collectLastValue(kosmos.promptViewModel.logoInfo) @@ -1766,6 +1776,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa logoBitmap: Bitmap? = null, logoDescription: String? = null, packageName: String = OP_PACKAGE_NAME_WITH_APP_LOGO, + userId: Int = USER_ID, block: suspend TestScope.() -> Unit, ) { val topActivity = ComponentName(packageName, "test app") @@ -1785,6 +1796,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa logoBitmapFromApp = if (logoRes != 0) logoDrawableFromAppRes.toBitmap() else logoBitmap, logoDescriptionFromApp = logoDescription, packageName = packageName, + userId = userId, ) kosmos.biometricStatusRepository.setFingerprintAcquiredStatus( @@ -2010,6 +2022,7 @@ private fun PromptSelectorInteractor.initializePrompt( logoBitmapFromApp: Bitmap? = null, logoDescriptionFromApp: String? = null, packageName: String = OP_PACKAGE_NAME_WITH_APP_LOGO, + userId: Int = USER_ID, ) { val info = PromptInfo().apply { @@ -2028,7 +2041,7 @@ private fun PromptSelectorInteractor.initializePrompt( setPrompt( info, - USER_ID, + userId, REQUEST_ID, BiometricModalities(fingerprintProperties = fingerprint, faceProperties = face), CHALLENGE, diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/log/CommunalMetricsLoggerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/log/CommunalMetricsLoggerTest.kt index 82918a569990..af2d7e45ee5f 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/log/CommunalMetricsLoggerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/log/CommunalMetricsLoggerTest.kt @@ -47,20 +47,14 @@ class CommunalMetricsLoggerTest : SysuiTestCase() { @Test fun logAddWidget_componentNotLoggable_doNotLog() { - underTest.logAddWidget( - componentName = "com.green.package/my_test_widget", - rank = 1, - ) + underTest.logAddWidget(componentName = "com.green.package/my_test_widget", rank = 1) verify(statsLogProxy, never()) - .writeCommunalHubWidgetEventReported(anyInt(), any(), anyInt()) + .writeCommunalHubWidgetEventReported(anyInt(), any(), anyInt(), anyInt()) } @Test fun logAddWidget_componentLoggable_logAddEvent() { - underTest.logAddWidget( - componentName = "com.blue.package/my_test_widget", - rank = 1, - ) + underTest.logAddWidget(componentName = "com.blue.package/my_test_widget", rank = 1) verify(statsLogProxy) .writeCommunalHubWidgetEventReported( SysUiStatsLog.COMMUNAL_HUB_WIDGET_EVENT_REPORTED__ACTION__ADD, @@ -71,20 +65,14 @@ class CommunalMetricsLoggerTest : SysuiTestCase() { @Test fun logRemoveWidget_componentNotLoggable_doNotLog() { - underTest.logRemoveWidget( - componentName = "com.yellow.package/my_test_widget", - rank = 2, - ) + underTest.logRemoveWidget(componentName = "com.yellow.package/my_test_widget", rank = 2) verify(statsLogProxy, never()) - .writeCommunalHubWidgetEventReported(anyInt(), any(), anyInt()) + .writeCommunalHubWidgetEventReported(anyInt(), any(), anyInt(), anyInt()) } @Test fun logRemoveWidget_componentLoggable_logRemoveEvent() { - underTest.logRemoveWidget( - componentName = "com.red.package/my_test_widget", - rank = 2, - ) + underTest.logRemoveWidget(componentName = "com.red.package/my_test_widget", rank = 2) verify(statsLogProxy) .writeCommunalHubWidgetEventReported( SysUiStatsLog.COMMUNAL_HUB_WIDGET_EVENT_REPORTED__ACTION__REMOVE, @@ -95,20 +83,14 @@ class CommunalMetricsLoggerTest : SysuiTestCase() { @Test fun logTapWidget_componentNotLoggable_doNotLog() { - underTest.logTapWidget( - componentName = "com.yellow.package/my_test_widget", - rank = 2, - ) + underTest.logTapWidget(componentName = "com.yellow.package/my_test_widget", rank = 2) verify(statsLogProxy, never()) - .writeCommunalHubWidgetEventReported(anyInt(), any(), anyInt()) + .writeCommunalHubWidgetEventReported(anyInt(), any(), anyInt(), anyInt()) } @Test fun logTapWidget_componentLoggable_logRemoveEvent() { - underTest.logTapWidget( - componentName = "com.red.package/my_test_widget", - rank = 2, - ) + underTest.logTapWidget(componentName = "com.red.package/my_test_widget", rank = 2) verify(statsLogProxy) .writeCommunalHubWidgetEventReported( SysUiStatsLog.COMMUNAL_HUB_WIDGET_EVENT_REPORTED__ACTION__TAP, @@ -140,4 +122,43 @@ class CommunalMetricsLoggerTest : SysuiTestCase() { ) assertThat(statsEvents).hasSize(1) } + + @Test + fun logResizeWidget_componentNotLoggable_doNotLog() { + underTest.logResizeWidget( + componentName = "com.green.package/my_test_widget", + rank = 1, + spanY = 2, + ) + verify(statsLogProxy, never()) + .writeCommunalHubWidgetEventReported(anyInt(), any(), anyInt(), anyInt()) + } + + @Test + fun logResizeWidget_componentLoggable_logResizeEvent() { + underTest.logResizeWidget( + componentName = "com.blue.package/my_test_widget", + rank = 1, + spanY = 2, + ) + verify(statsLogProxy) + .writeCommunalHubWidgetEventReported( + SysUiStatsLog.COMMUNAL_HUB_WIDGET_EVENT_REPORTED__ACTION__RESIZE, + "com.blue.package/my_test_widget", + rank = 1, + spanY = 2, + ) + } + + @Test + fun logResizeWidget_defaultSpanY_usesDefaultValue() { + underTest.logResizeWidget(componentName = "com.blue.package/my_test_widget", rank = 1) + verify(statsLogProxy) + .writeCommunalHubWidgetEventReported( + SysUiStatsLog.COMMUNAL_HUB_WIDGET_EVENT_REPORTED__ACTION__RESIZE, + "com.blue.package/my_test_widget", + rank = 1, + spanY = 0, + ) + } } 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 cecc11e5ffd4..6b851cb017e1 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 @@ -353,6 +353,32 @@ class CommunalEditModeViewModelTest : SysuiTestCase() { assertThat(event.contentDescription).isEqualTo("Test Clock widget added to lock screen") } + @Test + fun onResizeWidget_logsMetrics() = + testScope.runTest { + val appWidgetId = 123 + val spanY = 2 + val widgetIdToRankMap = mapOf(appWidgetId to 1) + val componentName = ComponentName("test.package", "TestWidget") + val rank = 1 + + underTest.onResizeWidget( + appWidgetId = appWidgetId, + spanY = spanY, + widgetIdToRankMap = widgetIdToRankMap, + componentName = componentName, + rank = rank, + ) + + verify(communalInteractor).resizeWidget(appWidgetId, spanY, widgetIdToRankMap) + verify(metricsLogger) + .logResizeWidget( + componentName = componentName.flattenToString(), + rank = rank, + spanY = spanY, + ) + } + private companion object { val MAIN_USER_INFO = UserInfo(0, "primary", UserInfo.FLAG_MAIN) const val WIDGET_PICKER_PACKAGE_NAME = "widget_picker_package_name" diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractorTest.kt index 160865d625f5..f0d79bb83652 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractorTest.kt @@ -17,6 +17,8 @@ package com.android.systemui.deviceentry.domain.interactor import android.content.pm.UserInfo +import android.os.PowerManager +import android.provider.Settings import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.widget.LockPatternUtils @@ -31,22 +33,26 @@ import com.android.systemui.flags.fakeSystemPropertiesHelper import com.android.systemui.keyguard.data.repository.fakeBiometricSettingsRepository import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFaceAuthRepository import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository -import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository +import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository import com.android.systemui.keyguard.data.repository.fakeTrustRepository -import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.AuthenticationFlags -import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus import com.android.systemui.kosmos.testScope import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest import com.android.systemui.power.domain.interactor.powerInteractor +import com.android.systemui.scene.domain.interactor.sceneInteractor +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.testKosmos import com.android.systemui.user.data.model.SelectionStatus import com.android.systemui.user.data.repository.fakeUserRepository +import com.android.systemui.user.domain.interactor.selectedUserInteractor +import com.android.systemui.util.settings.fakeSettings import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.map import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.advanceTimeBy import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before @@ -228,6 +234,8 @@ class DeviceUnlockedInteractorTest : SysuiTestCase() { @Test fun deviceUnlockStatus_isResetToFalse_whenDeviceGoesToSleep() = testScope.runTest { + setLockAfterScreenTimeout(0) + kosmos.fakeAuthenticationRepository.powerButtonInstantlyLocks = false val deviceUnlockStatus by collectLastValue(underTest.deviceUnlockStatus) kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus( @@ -243,35 +251,54 @@ class DeviceUnlockedInteractorTest : SysuiTestCase() { } @Test - fun deviceUnlockStatus_becomesUnlocked_whenFingerprintUnlocked_whileDeviceAsleepInAod() = + fun deviceUnlockStatus_isResetToFalse_whenDeviceGoesToSleep_afterDelay() = testScope.runTest { + val delay = 5000 + setLockAfterScreenTimeout(delay) + kosmos.fakeAuthenticationRepository.powerButtonInstantlyLocks = false val deviceUnlockStatus by collectLastValue(underTest.deviceUnlockStatus) - assertThat(deviceUnlockStatus?.isUnlocked).isFalse() - kosmos.fakeKeyguardTransitionRepository.sendTransitionSteps( - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.AOD, - testScope = this, + kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus( + SuccessFingerprintAuthenticationStatus(0, true) ) + runCurrent() + assertThat(deviceUnlockStatus?.isUnlocked).isTrue() + kosmos.powerInteractor.setAsleepForTest() runCurrent() + assertThat(deviceUnlockStatus?.isUnlocked).isTrue() + advanceTimeBy(delay.toLong()) assertThat(deviceUnlockStatus?.isUnlocked).isFalse() + } + + @Test + fun deviceUnlockStatus_isResetToFalse_whenDeviceGoesToSleep_powerButtonLocksInstantly() = + testScope.runTest { + setLockAfterScreenTimeout(5000) + kosmos.fakeAuthenticationRepository.powerButtonInstantlyLocks = true + val deviceUnlockStatus by collectLastValue(underTest.deviceUnlockStatus) kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus( SuccessFingerprintAuthenticationStatus(0, true) ) runCurrent() assertThat(deviceUnlockStatus?.isUnlocked).isTrue() + + kosmos.powerInteractor.setAsleepForTest( + sleepReason = PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON + ) + runCurrent() + + assertThat(deviceUnlockStatus?.isUnlocked).isFalse() } @Test - fun deviceUnlockStatus_staysLocked_whenFingerprintUnlocked_whileDeviceAsleep() = + fun deviceUnlockStatus_becomesUnlocked_whenFingerprintUnlocked_whileDeviceAsleep() = testScope.runTest { + setLockAfterScreenTimeout(0) val deviceUnlockStatus by collectLastValue(underTest.deviceUnlockStatus) assertThat(deviceUnlockStatus?.isUnlocked).isFalse() - assertThat(kosmos.keyguardTransitionInteractor.getCurrentState()) - .isEqualTo(KeyguardState.LOCKSCREEN) kosmos.powerInteractor.setAsleepForTest() runCurrent() @@ -282,7 +309,7 @@ class DeviceUnlockedInteractorTest : SysuiTestCase() { SuccessFingerprintAuthenticationStatus(0, true) ) runCurrent() - assertThat(deviceUnlockStatus?.isUnlocked).isFalse() + assertThat(deviceUnlockStatus?.isUnlocked).isTrue() } @Test @@ -478,6 +505,98 @@ class DeviceUnlockedInteractorTest : SysuiTestCase() { .isEqualTo(DeviceEntryRestrictionReason.DeviceNotUnlockedSinceMainlineUpdate) } + @Test + fun deviceUnlockStatus_locksImmediately_whenDreamStarts_noTimeout() = + testScope.runTest { + setLockAfterScreenTimeout(0) + val isUnlocked by collectLastValue(underTest.deviceUnlockStatus.map { it.isUnlocked }) + unlockDevice() + + startDreaming() + + assertThat(isUnlocked).isFalse() + } + + @Test + fun deviceUnlockStatus_locksWithDelay_afterDreamStarts_withTimeout() = + testScope.runTest { + val delay = 5000 + setLockAfterScreenTimeout(delay) + val isUnlocked by collectLastValue(underTest.deviceUnlockStatus.map { it.isUnlocked }) + unlockDevice() + + startDreaming() + assertThat(isUnlocked).isTrue() + + advanceTimeBy(delay - 1L) + assertThat(isUnlocked).isTrue() + + advanceTimeBy(1L) + assertThat(isUnlocked).isFalse() + } + + @Test + fun deviceUnlockStatus_doesNotLockWithDelay_whenDreamStopsBeforeTimeout() = + testScope.runTest { + val delay = 5000 + setLockAfterScreenTimeout(delay) + val isUnlocked by collectLastValue(underTest.deviceUnlockStatus.map { it.isUnlocked }) + unlockDevice() + + startDreaming() + assertThat(isUnlocked).isTrue() + + advanceTimeBy(delay - 1L) + assertThat(isUnlocked).isTrue() + + stopDreaming() + assertThat(isUnlocked).isTrue() + + advanceTimeBy(1L) + assertThat(isUnlocked).isTrue() + } + + @Test + fun deviceUnlockStatus_doesNotLock_whenDreamStarts_ifNotInteractive() = + testScope.runTest { + setLockAfterScreenTimeout(0) + val isUnlocked by collectLastValue(underTest.deviceUnlockStatus.map { it.isUnlocked }) + unlockDevice() + + startDreaming() + + assertThat(isUnlocked).isFalse() + } + + private fun TestScope.unlockDevice() { + val deviceUnlockStatus by collectLastValue(underTest.deviceUnlockStatus) + + kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus( + SuccessFingerprintAuthenticationStatus(0, true) + ) + assertThat(deviceUnlockStatus?.isUnlocked).isTrue() + kosmos.sceneInteractor.changeScene(Scenes.Gone, "reason") + runCurrent() + } + + private fun setLockAfterScreenTimeout(timeoutMs: Int) { + kosmos.fakeSettings.putIntForUser( + Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT, + timeoutMs, + kosmos.selectedUserInteractor.getSelectedUserId(), + ) + } + + private fun TestScope.startDreaming() { + kosmos.fakeKeyguardRepository.setDreaming(true) + runCurrent() + } + + private fun TestScope.stopDreaming() { + kosmos.fakeKeyguardRepository.setDreaming(false) + runCurrent() + } + private fun TestScope.verifyRestrictionReasonsForAuthFlags( vararg authFlagToDeviceEntryRestriction: Pair<Int, DeviceEntryRestrictionReason?> ) { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractorTest.kt index 5df9b7b8d5b8..2efa2f34e394 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractorTest.kt @@ -85,63 +85,62 @@ class TutorialSchedulerInteractorTest : SysuiTestCase() { @Test fun connectKeyboard_delayElapse_launchForKeyboard() = testScope.runTest { - launchAndAssert(TutorialType.KEYBOARD) - keyboardRepository.setIsAnyKeyboardConnected(true) advanceTimeBy(LAUNCH_DELAY) + + launchAndAssert(TutorialType.KEYBOARD) } @Test fun connectBothDevices_delayElapse_launchForBoth() = testScope.runTest { - launchAndAssert(TutorialType.BOTH) - keyboardRepository.setIsAnyKeyboardConnected(true) touchpadRepository.setIsAnyTouchpadConnected(true) advanceTimeBy(LAUNCH_DELAY) + + launchAndAssert(TutorialType.BOTH) } @Test fun connectBothDevice_delayNotElapse_launchNothing() = testScope.runTest { - launchAndAssert(TutorialType.NONE) - keyboardRepository.setIsAnyKeyboardConnected(true) touchpadRepository.setIsAnyTouchpadConnected(true) advanceTimeBy(A_SHORT_PERIOD_OF_TIME) + + launchAndAssert(TutorialType.NONE) } @Test fun nothingConnect_delayElapse_launchNothing() = testScope.runTest { - launchAndAssert(TutorialType.NONE) - keyboardRepository.setIsAnyKeyboardConnected(false) touchpadRepository.setIsAnyTouchpadConnected(false) advanceTimeBy(LAUNCH_DELAY) + + launchAndAssert(TutorialType.NONE) } @Test fun connectKeyboard_thenTouchpad_delayElapse_launchForBoth() = testScope.runTest { - launchAndAssert(TutorialType.BOTH) - keyboardRepository.setIsAnyKeyboardConnected(true) advanceTimeBy(A_SHORT_PERIOD_OF_TIME) touchpadRepository.setIsAnyTouchpadConnected(true) advanceTimeBy(REMAINING_TIME) + + launchAndAssert(TutorialType.BOTH) } @Test fun connectKeyboard_thenTouchpad_removeKeyboard_delayElapse_launchNothing() = testScope.runTest { - launchAndAssert(TutorialType.NONE) - keyboardRepository.setIsAnyKeyboardConnected(true) advanceTimeBy(A_SHORT_PERIOD_OF_TIME) touchpadRepository.setIsAnyTouchpadConnected(true) keyboardRepository.setIsAnyKeyboardConnected(false) advanceTimeBy(REMAINING_TIME) + launchAndAssert(TutorialType.NONE) } private suspend fun launchAndAssert(expectedTutorial: TutorialType) = diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepositoryTest.kt new file mode 100644 index 000000000000..c646d8f11706 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepositoryTest.kt @@ -0,0 +1,291 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.keyboard.shortcut.data.repository + +import android.content.Context +import android.content.Context.INPUT_SERVICE +import android.hardware.input.InputGestureData +import android.hardware.input.InputGestureData.createKeyTrigger +import android.hardware.input.KeyGestureEvent +import android.hardware.input.fakeInputManager +import android.platform.test.annotations.DisableFlags +import android.platform.test.annotations.EnableFlags +import android.view.KeyEvent +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.hardware.input.Flags.FLAG_ENABLE_CUSTOMIZABLE_INPUT_GESTURES +import com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.keyboard.shortcut.customShortcutCategoriesRepository +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.ShortcutCategoryType +import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.AppCategories +import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.MultiTasking +import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.System +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.keyboard.shortcut.shortcutHelperTestHelper +import com.android.systemui.kosmos.testScope +import com.android.systemui.settings.FakeUserTracker +import com.android.systemui.settings.userTracker +import com.android.systemui.testKosmos +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.kotlin.anyOrNull +import org.mockito.kotlin.mock +import org.mockito.kotlin.whenever + +@SmallTest +@RunWith(AndroidJUnit4::class) +class CustomShortcutCategoriesRepositoryTest : SysuiTestCase() { + + private val mockUserContext: Context = mock() + private val kosmos = + testKosmos().also { + it.userTracker = FakeUserTracker(onCreateCurrentUserContext = { mockUserContext }) + } + + private val fakeInputManager = kosmos.fakeInputManager + private val testScope = kosmos.testScope + private val helper = kosmos.shortcutHelperTestHelper + private val repo = kosmos.customShortcutCategoriesRepository + + @Before + fun setup() { + whenever(mockUserContext.getSystemService(INPUT_SERVICE)) + .thenReturn(fakeInputManager.inputManager) + } + + @Test + @EnableFlags(FLAG_ENABLE_CUSTOMIZABLE_INPUT_GESTURES, FLAG_USE_KEY_GESTURE_EVENT_HANDLER) + fun categories_emitsCorrectlyConvertedShortcutCategories() { + testScope.runTest { + whenever( + fakeInputManager.inputManager.getCustomInputGestures(/* filter= */ anyOrNull()) + ) + .thenReturn(customizableInputGesturesWithSimpleShortcutCombinations) + + helper.toggle(deviceId = 123) + val categories by collectLastValue(repo.categories) + + assertThat(categories) + .containsExactlyElementsIn(expectedShortcutCategoriesWithSimpleShortcutCombination) + } + } + + @Test + @DisableFlags(FLAG_ENABLE_CUSTOMIZABLE_INPUT_GESTURES, FLAG_USE_KEY_GESTURE_EVENT_HANDLER) + fun categories_emitsEmptyListWhenFlagIsDisabled() { + testScope.runTest { + whenever( + fakeInputManager.inputManager.getCustomInputGestures(/* filter= */ anyOrNull()) + ) + .thenReturn(customizableInputGesturesWithSimpleShortcutCombinations) + + helper.toggle(deviceId = 123) + val categories by collectLastValue(repo.categories) + + assertThat(categories).isEmpty() + } + } + + @Test + @EnableFlags(FLAG_ENABLE_CUSTOMIZABLE_INPUT_GESTURES, FLAG_USE_KEY_GESTURE_EVENT_HANDLER) + fun categories_ignoresUnknownKeyGestureTypes() { + testScope.runTest { + whenever( + fakeInputManager.inputManager.getCustomInputGestures(/* filter= */ anyOrNull()) + ) + .thenReturn(customizableInputGestureWithUnknownKeyGestureType) + + helper.toggle(deviceId = 123) + val categories by collectLastValue(repo.categories) + + assertThat(categories).isEmpty() + } + } + + private fun simpleInputGestureData( + keyCode: Int = KeyEvent.KEYCODE_A, + modifiers: Int = KeyEvent.META_CTRL_ON or KeyEvent.META_ALT_ON, + keyGestureType: Int, + ): InputGestureData { + val builder = InputGestureData.Builder() + builder.setKeyGestureType(keyGestureType) + builder.setTrigger(createKeyTrigger(keyCode, modifiers)) + return builder.build() + } + + private fun simpleShortcutCategory( + category: ShortcutCategoryType, + subcategoryLabel: String, + shortcutLabel: String, + ): ShortcutCategory { + return ShortcutCategory( + type = category, + subCategories = + listOf( + ShortcutSubCategory( + label = subcategoryLabel, + shortcuts = listOf(simpleShortcut(shortcutLabel)), + ) + ), + ) + } + + private fun simpleShortcut(label: String) = + Shortcut( + label = label, + commands = + listOf( + ShortcutCommand( + isCustom = true, + keys = + listOf( + ShortcutKey.Text("Ctrl"), + ShortcutKey.Text("Alt"), + ShortcutKey.Text("A"), + ), + ) + ), + ) + + private val customizableInputGestureWithUnknownKeyGestureType = + // These key gesture events are currently not supported by shortcut helper customizer + listOf( + simpleInputGestureData( + keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_GLOBAL_ACTIONS + ), + simpleInputGestureData(keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_MEDIA_KEY), + ) + + private val expectedShortcutCategoriesWithSimpleShortcutCombination = + listOf( + simpleShortcutCategory(System, "System apps", "Open assistant"), + simpleShortcutCategory(System, "System controls", "Go to home screen"), + simpleShortcutCategory(System, "System apps", "Open settings"), + simpleShortcutCategory(System, "System controls", "Lock screen"), + simpleShortcutCategory(System, "System controls", "View notifications"), + simpleShortcutCategory(System, "System apps", "Take a note"), + simpleShortcutCategory(System, "System controls", "Take screenshot"), + simpleShortcutCategory(System, "System controls", "Go back"), + simpleShortcutCategory( + MultiTasking, + "Split screen", + "Switch from split screen to full screen", + ), + simpleShortcutCategory( + MultiTasking, + "Split screen", + "Use split screen with current app on the left", + ), + simpleShortcutCategory( + MultiTasking, + "Split screen", + "Switch to app on left or above while using split screen", + ), + simpleShortcutCategory( + MultiTasking, + "Split screen", + "Use split screen with current app on the right", + ), + simpleShortcutCategory( + MultiTasking, + "Split screen", + "Switch to app on right or below while using split screen", + ), + simpleShortcutCategory(System, "System controls", "Show shortcuts"), + simpleShortcutCategory(System, "System controls", "View recent apps"), + simpleShortcutCategory(AppCategories, "Applications", "Calculator"), + simpleShortcutCategory(AppCategories, "Applications", "Calendar"), + simpleShortcutCategory(AppCategories, "Applications", "Chrome"), + simpleShortcutCategory(AppCategories, "Applications", "Contacts"), + simpleShortcutCategory(AppCategories, "Applications", "Gmail"), + simpleShortcutCategory(AppCategories, "Applications", "Maps"), + simpleShortcutCategory(AppCategories, "Applications", "Messages"), + simpleShortcutCategory(MultiTasking, "Recent apps", "Cycle forward through recent apps"), + ) + + private val customizableInputGesturesWithSimpleShortcutCombinations = + listOf( + simpleInputGestureData( + keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT + ), + simpleInputGestureData(keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_HOME), + simpleInputGestureData( + keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS + ), + simpleInputGestureData(keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LOCK_SCREEN), + simpleInputGestureData( + keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL + ), + simpleInputGestureData(keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_NOTES), + simpleInputGestureData( + keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT + ), + simpleInputGestureData(keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_BACK), + simpleInputGestureData( + keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION + ), + simpleInputGestureData( + keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT + ), + simpleInputGestureData( + keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_LEFT + ), + simpleInputGestureData( + keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT + ), + simpleInputGestureData( + keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT + ), + simpleInputGestureData( + keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER + ), + simpleInputGestureData(keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS), + simpleInputGestureData( + keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR + ), + simpleInputGestureData( + keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR + ), + simpleInputGestureData( + keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER + ), + simpleInputGestureData( + keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS + ), + simpleInputGestureData( + keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL + ), + simpleInputGestureData( + keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS + ), + simpleInputGestureData( + keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING + ), + simpleInputGestureData( + keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER + ), + ) +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperCategoriesRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/DefaultShortcutCategoriesRepositoryTest.kt index 620b8b63c71a..f90ab1fcc75b 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperCategoriesRepositoryTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/DefaultShortcutCategoriesRepositoryTest.kt @@ -40,6 +40,7 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.keyboard.shortcut.data.source.FakeKeyboardShortcutGroupsSource import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts +import com.android.systemui.keyboard.shortcut.defaultShortcutCategoriesRepository 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.ShortcutCategoryType @@ -47,7 +48,6 @@ 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.keyboard.shortcut.shortcutHelperAppCategoriesShortcutsSource -import com.android.systemui.keyboard.shortcut.shortcutHelperCategoriesRepository import com.android.systemui.keyboard.shortcut.shortcutHelperCurrentAppShortcutsSource import com.android.systemui.keyboard.shortcut.shortcutHelperInputShortcutsSource import com.android.systemui.keyboard.shortcut.shortcutHelperMultiTaskingShortcutsSource @@ -71,7 +71,7 @@ import org.mockito.kotlin.whenever @OptIn(ExperimentalCoroutinesApi::class) @SmallTest @RunWith(AndroidJUnit4::class) -class ShortcutHelperCategoriesRepositoryTest : SysuiTestCase() { +class DefaultShortcutCategoriesRepositoryTest : SysuiTestCase() { private val fakeSystemSource = FakeKeyboardShortcutGroupsSource() private val fakeMultiTaskingSource = FakeKeyboardShortcutGroupsSource() @@ -87,7 +87,7 @@ class ShortcutHelperCategoriesRepositoryTest : SysuiTestCase() { it.shortcutHelperCurrentAppShortcutsSource = FakeKeyboardShortcutGroupsSource() } - private val repo = kosmos.shortcutHelperCategoriesRepository + private val repo = kosmos.defaultShortcutCategoriesRepository private val helper = kosmos.shortcutHelperTestHelper private val testScope = kosmos.testScope private val fakeInputManager = kosmos.fakeInputManager diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt index bfe89de6229d..3d5498b61471 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt @@ -16,11 +16,14 @@ package com.android.systemui.keyguard.data.repository +import android.animation.Animator import android.animation.ValueAnimator +import android.platform.test.annotations.EnableFlags import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.FlakyTest import androidx.test.filters.SmallTest import com.android.app.animation.Interpolators +import com.android.systemui.Flags import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectValues import com.android.systemui.keyguard.shared.model.KeyguardState @@ -41,6 +44,8 @@ import com.google.common.truth.Truth.assertThat import java.math.BigDecimal import java.math.RoundingMode import java.util.UUID +import kotlin.test.assertEquals +import kotlin.test.assertTrue import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.flow.dropWhile @@ -53,6 +58,7 @@ import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import org.mockito.kotlin.mock @SmallTest @RunWith(AndroidJUnit4::class) @@ -65,6 +71,8 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() { private lateinit var underTest: KeyguardTransitionRepository private lateinit var runner: KeyguardTransitionRunner + private val animatorListener = mock<Animator.AnimatorListener>() + @Before fun setUp() { underTest = KeyguardTransitionRepositoryImpl(Dispatchers.Main) @@ -80,7 +88,7 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() { runner.startTransition( this, TransitionInfo(OWNER_NAME, AOD, LOCKSCREEN, getAnimator()), - maxFrames = 100 + maxFrames = 100, ) assertSteps(steps, listWithStep(BigDecimal(.1)), AOD, LOCKSCREEN) @@ -107,7 +115,7 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() { LOCKSCREEN, AOD, getAnimator(), - TransitionModeOnCanceled.LAST_VALUE + TransitionModeOnCanceled.LAST_VALUE, ), ) @@ -142,7 +150,7 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() { LOCKSCREEN, AOD, getAnimator(), - TransitionModeOnCanceled.RESET + TransitionModeOnCanceled.RESET, ), ) @@ -177,7 +185,7 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() { LOCKSCREEN, AOD, getAnimator(), - TransitionModeOnCanceled.REVERSE + TransitionModeOnCanceled.REVERSE, ), ) @@ -476,6 +484,49 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() { assertThat(steps.size).isEqualTo(3) } + @Test + @EnableFlags(Flags.FLAG_KEYGUARD_TRANSITION_FORCE_FINISH_ON_SCREEN_OFF) + fun forceFinishCurrentTransition_noFurtherStepsEmitted() = + testScope.runTest { + val steps by collectValues(underTest.transitions.dropWhile { step -> step.from == OFF }) + + var sentForceFinish = false + + runner.startTransition( + this, + TransitionInfo(OWNER_NAME, AOD, LOCKSCREEN, getAnimator()), + maxFrames = 100, + // Force-finish on the second frame. + frameCallback = { frameNumber -> + if (!sentForceFinish && frameNumber > 1) { + testScope.launch { underTest.forceFinishCurrentTransition() } + sentForceFinish = true + } + }, + ) + + val lastTwoRunningSteps = + steps.filter { it.transitionState == TransitionState.RUNNING }.takeLast(2) + + // Make sure we stopped emitting RUNNING steps early, but then emitted a final 1f step. + assertTrue(lastTwoRunningSteps[0].value < 0.5f) + assertTrue(lastTwoRunningSteps[1].value == 1f) + + assertEquals(steps.last().from, AOD) + assertEquals(steps.last().to, LOCKSCREEN) + assertEquals(steps.last().transitionState, TransitionState.FINISHED) + } + + @Test + @EnableFlags(Flags.FLAG_KEYGUARD_TRANSITION_FORCE_FINISH_ON_SCREEN_OFF) + fun forceFinishCurrentTransition_noTransitionStarted_noStepsEmitted() = + testScope.runTest { + val steps by collectValues(underTest.transitions.dropWhile { step -> step.from == OFF }) + + underTest.forceFinishCurrentTransition() + assertEquals(0, steps.size) + } + private fun listWithStep( step: BigDecimal, start: BigDecimal = BigDecimal.ZERO, @@ -505,7 +556,7 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() { to, fractions[0].toFloat(), TransitionState.STARTED, - OWNER_NAME + OWNER_NAME, ) ) fractions.forEachIndexed { index, fraction -> @@ -519,7 +570,7 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() { to, fraction.toFloat(), TransitionState.RUNNING, - OWNER_NAME + OWNER_NAME, ) ) } @@ -538,6 +589,7 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() { return ValueAnimator().apply { setInterpolator(Interpolators.LINEAR) setDuration(10) + addListener(animatorListener) } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractorTest.kt index 8914c80cdd5e..ae2a5c5fe501 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardOcclusionInteractorTest.kt @@ -34,6 +34,7 @@ package com.android.systemui.keyguard.domain.interactor +import android.provider.Settings import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase @@ -43,6 +44,7 @@ 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.KeyguardViewMediator import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository import com.android.systemui.keyguard.data.repository.deviceEntryFingerprintAuthRepository import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository @@ -60,10 +62,13 @@ import com.android.systemui.scene.data.repository.setSceneTransition import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.statusbar.domain.interactor.keyguardOcclusionInteractor import com.android.systemui.testKosmos +import com.android.systemui.util.settings.data.repository.userAwareSecureSettingsRepository import com.google.common.truth.Truth.assertThat import junit.framework.Assert.assertFalse import junit.framework.Assert.assertTrue import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.advanceTimeBy import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before @@ -96,7 +101,7 @@ class KeyguardOcclusionInteractorTest : SysuiTestCase() { from = KeyguardState.LOCKSCREEN, to = KeyguardState.AOD, testScope = testScope, - throughTransitionState = TransitionState.RUNNING + throughTransitionState = TransitionState.RUNNING, ) powerInteractor.onCameraLaunchGestureDetected() @@ -134,7 +139,7 @@ class KeyguardOcclusionInteractorTest : SysuiTestCase() { from = KeyguardState.AOD, to = KeyguardState.LOCKSCREEN, testScope = testScope, - throughTransitionState = TransitionState.RUNNING + throughTransitionState = TransitionState.RUNNING, ) powerInteractor.onCameraLaunchGestureDetected() @@ -182,21 +187,12 @@ class KeyguardOcclusionInteractorTest : SysuiTestCase() { kosmos.keyguardOcclusionRepository.setShowWhenLockedActivityInfo(true) runCurrent() - assertThat(values) - .containsExactly( - false, - true, - ) + assertThat(values).containsExactly(false, true) kosmos.keyguardOcclusionRepository.setShowWhenLockedActivityInfo(false) runCurrent() - assertThat(values) - .containsExactly( - false, - true, - false, - ) + assertThat(values).containsExactly(false, true, false) kosmos.keyguardOcclusionRepository.setShowWhenLockedActivityInfo(true) runCurrent() @@ -228,7 +224,7 @@ class KeyguardOcclusionInteractorTest : SysuiTestCase() { from = KeyguardState.GONE, to = KeyguardState.AOD, testScope = testScope, - throughTransitionState = TransitionState.RUNNING + throughTransitionState = TransitionState.RUNNING, ) powerInteractor.onCameraLaunchGestureDetected() @@ -242,10 +238,7 @@ class KeyguardOcclusionInteractorTest : SysuiTestCase() { testScope = testScope, ) - assertThat(values) - .containsExactly( - false, - ) + assertThat(values).containsExactly(false) } @Test @@ -263,7 +256,7 @@ class KeyguardOcclusionInteractorTest : SysuiTestCase() { from = KeyguardState.UNDEFINED, to = KeyguardState.AOD, testScope = testScope, - throughTransitionState = TransitionState.RUNNING + throughTransitionState = TransitionState.RUNNING, ) powerInteractor.onCameraLaunchGestureDetected() @@ -278,10 +271,7 @@ class KeyguardOcclusionInteractorTest : SysuiTestCase() { testScope = testScope, ) - assertThat(values) - .containsExactly( - false, - ) + assertThat(values).containsExactly(false) } @Test @@ -304,8 +294,19 @@ class KeyguardOcclusionInteractorTest : SysuiTestCase() { assertThat(occludingActivityWillDismissKeyguard).isTrue() // Re-lock device: - kosmos.powerInteractor.setAsleepForTest() - runCurrent() + lockDevice() assertThat(occludingActivityWillDismissKeyguard).isFalse() } + + private suspend fun TestScope.lockDevice() { + kosmos.powerInteractor.setAsleepForTest() + advanceTimeBy( + kosmos.userAwareSecureSettingsRepository + .getInt( + Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT, + KeyguardViewMediator.KEYGUARD_LOCK_AFTER_DELAY_DEFAULT, + ) + .toLong() + ) + } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt index ecc62e908a4f..87ab3c89a671 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt @@ -69,7 +69,9 @@ class ClockSectionTest : SysuiTestCase() { get() = kosmos.fakeSystemBarUtilsProxy.getStatusBarHeight() + context.resources.getDimensionPixelSize(customR.dimen.small_clock_padding_top) + - context.resources.getDimensionPixelSize(R.dimen.keyguard_smartspace_top_offset) + context.resources.getDimensionPixelSize( + customR.dimen.keyguard_smartspace_top_offset + ) private val LARGE_CLOCK_TOP get() = diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/util/KeyguardTransitionRunner.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/util/KeyguardTransitionRunner.kt index 1abb441439fe..5798e0776c4f 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/util/KeyguardTransitionRunner.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/util/KeyguardTransitionRunner.kt @@ -21,6 +21,7 @@ import android.animation.ValueAnimator import android.view.Choreographer.FrameCallback import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository import com.android.systemui.keyguard.shared.model.TransitionInfo +import java.util.function.Consumer import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job @@ -35,9 +36,8 @@ import org.junit.Assert.fail * Gives direct control over ValueAnimator, in order to make transition tests deterministic. See * [AnimationHandler]. Animators are required to be run on the main thread, so dispatch accordingly. */ -class KeyguardTransitionRunner( - val repository: KeyguardTransitionRepository, -) : AnimationFrameCallbackProvider { +class KeyguardTransitionRunner(val repository: KeyguardTransitionRepository) : + AnimationFrameCallbackProvider { private var frameCount = 1L private var frames = MutableStateFlow(Pair<Long, FrameCallback?>(0L, null)) @@ -48,7 +48,12 @@ class KeyguardTransitionRunner( * For transitions being directed by an animator. Will control the number of frames being * generated so the values are deterministic. */ - suspend fun startTransition(scope: CoroutineScope, info: TransitionInfo, maxFrames: Int = 100) { + suspend fun startTransition( + scope: CoroutineScope, + info: TransitionInfo, + maxFrames: Int = 100, + frameCallback: Consumer<Long>? = null, + ) { // AnimationHandler uses ThreadLocal storage, and ValueAnimators MUST start from main // thread withContext(Dispatchers.Main) { @@ -62,7 +67,12 @@ class KeyguardTransitionRunner( isTerminated = frameNumber >= maxFrames if (!isTerminated) { - withContext(Dispatchers.Main) { callback?.doFrame(frameNumber) } + try { + withContext(Dispatchers.Main) { callback?.doFrame(frameNumber) } + frameCallback?.accept(frameNumber) + } catch (e: IllegalStateException) { + e.printStackTrace() + } } } } @@ -90,9 +100,13 @@ class KeyguardTransitionRunner( override fun postFrameCallback(cb: FrameCallback) { frames.value = Pair(frameCount++, cb) } + override fun postCommitCallback(runnable: Runnable) {} + override fun getFrameTime() = frameCount + override fun getFrameDelay() = 1L + override fun setFrameDelay(delay: Long) {} companion object { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/pipeline/Media3ActionFactoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/pipeline/Media3ActionFactoryTest.kt index 8e67e602abd9..f8f6fe246563 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/pipeline/Media3ActionFactoryTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/pipeline/Media3ActionFactoryTest.kt @@ -38,6 +38,7 @@ import com.android.systemui.media.controls.util.fakeMediaControllerFactory import com.android.systemui.media.controls.util.fakeSessionTokenFactory import com.android.systemui.res.R import com.android.systemui.testKosmos +import com.android.systemui.util.concurrency.execution import com.google.common.collect.ImmutableList import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.test.runCurrent @@ -105,6 +106,7 @@ class Media3ActionFactoryTest : SysuiTestCase() { kosmos.looper, handler, kosmos.testScope, + kosmos.execution, ) controllerFactory.setMedia3Controller(media3Controller) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerControllerTest.java index 8ccaf6bc0651..0f631509bfba 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerControllerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerControllerTest.java @@ -300,7 +300,7 @@ public class QRCodeScannerControllerTest extends SysuiTestCase { mSecureSettings.putStringForUser(LOCK_SCREEN_SHOW_QR_CODE_SCANNER, "0", UserHandle.USER_CURRENT); verifyActivityDetails("abc/.def"); - assertThat(mController.isEnabledForLockScreenButton()).isFalse(); + assertThat(mController.isEnabledForLockScreenButton()).isTrue(); assertThat(mController.isAllowedOnLockScreen()).isTrue(); assertThat(mController.isAbleToLaunchScannerActivity()).isTrue(); 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 5cba325450e6..b72668bf5be6 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 @@ -18,6 +18,7 @@ package com.android.systemui.qs.composefragment.viewmodel 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 @@ -43,6 +44,7 @@ import com.android.systemui.statusbar.StatusBarState import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel import com.android.systemui.statusbar.disableflags.data.repository.fakeDisableFlagsRepository import com.android.systemui.statusbar.sysuiStatusBarStateController +import com.android.systemui.util.animation.DisappearParameters import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.TestScope @@ -318,6 +320,149 @@ class QSFragmentComposeViewModelTest : AbstractQSFragmentComposeViewModelTest() } } + @Test + fun qqsMediaExpansion_collapsedMediaInLandscape() = + with(kosmos) { + testScope.testWithinLifecycle { + setCollapsedMediaInLandscape(true) + 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) + } + } + + @Test + fun qqsMediaExpansion_notCollapsedMediaInLandscape_alwaysExpanded() = + with(kosmos) { + testScope.testWithinLifecycle { + setCollapsedMediaInLandscape(false) + 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) + } + } + + @Test + fun qqsMediaExpansion_reactsToChangesInCollapsedMediaInLandscape() = + with(kosmos) { + testScope.testWithinLifecycle { + setConfigurationForMediaInRow(mediaInRow = true) + 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) + } + } + + @Test + fun applyQsScrollPositionForClipping() = + with(kosmos) { + testScope.testWithinLifecycle { + val left = 1f + val top = 3f + val right = 5f + val bottom = 7f + + underTest.applyNewQsScrollerBounds(left, top, right, bottom) + + assertThat(qsMediaHost.currentClipping) + .isEqualTo(Rect(left.toInt(), top.toInt(), right.toInt(), bottom.toInt())) + } + } + + @Test + fun shouldUpdateMediaSquishiness_inSplitShadeFalse_mediaSquishinessSet() = + with(kosmos) { + testScope.testWithinLifecycle { + underTest.isInSplitShade = false + 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) + } + } + + @Test + fun inSplitShade_differentStatusBarState_mediaSquishinessSet() = + with(kosmos) { + testScope.testWithinLifecycle { + underTest.isInSplitShade = true + 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) + } + } + + @Test + fun disappearParams() = + with(kosmos) { + testScope.testWithinLifecycle { + setMediaState(ACTIVE_MEDIA) + + setConfigurationForMediaInRow(false) + Snapshot.sendApplyNotifications() + runCurrent() + + assertThat(underTest.qqsMediaHost.disappearParameters) + .isEqualTo(disappearParamsColumn) + assertThat(underTest.qsMediaHost.disappearParameters) + .isEqualTo(disappearParamsColumn) + + setConfigurationForMediaInRow(true) + Snapshot.sendApplyNotifications() + runCurrent() + + assertThat(underTest.qqsMediaHost.disappearParameters).isEqualTo(disappearParamsRow) + assertThat(underTest.qsMediaHost.disappearParameters).isEqualTo(disappearParamsRow) + } + } + private fun TestScope.setMediaState(state: MediaState) { with(kosmos) { val activeMedia = state == ACTIVE_MEDIA @@ -331,6 +476,14 @@ class QSFragmentComposeViewModelTest : AbstractQSFragmentComposeViewModelTest() runCurrent() } + private fun TestScope.setCollapsedMediaInLandscape(collapsed: Boolean) { + with(kosmos) { + overrideResource(R.bool.config_quickSettingsMediaLandscapeCollapsed, collapsed) + fakeConfigurationRepository.onAnyConfigurationChange() + } + runCurrent() + } + companion object { private const val QS_DISABLE_FLAG = StatusBarManager.DISABLE2_QUICK_SETTINGS @@ -339,6 +492,26 @@ class QSFragmentComposeViewModelTest : AbstractQSFragmentComposeViewModelTest() } private const val epsilon = 0.001f + + private val disappearParamsColumn = + DisappearParameters().apply { + fadeStartPosition = 0.95f + disappearStart = 0f + disappearEnd = 0.95f + disappearSize.set(1f, 0f) + gonePivot.set(0f, 0f) + contentTranslationFraction.set(0f, 1f) + } + + private val disappearParamsRow = + DisappearParameters().apply { + fadeStartPosition = 0.95f + disappearStart = 0f + disappearEnd = 0.6f + disappearSize.set(0f, 0.4f) + gonePivot.set(1f, 0f) + contentTranslationFraction.set(0.25f, 1f) + } } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/QSPreferencesRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/QSPreferencesRepositoryTest.kt index dda9cd5529a5..4dcbdfae6d4a 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/QSPreferencesRepositoryTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/data/repository/QSPreferencesRepositoryTest.kt @@ -104,67 +104,6 @@ class QSPreferencesRepositoryTest : SysuiTestCase() { } } - @Test - fun showLabels_updatesFromSharedPreferences() = - with(kosmos) { - testScope.runTest { - val latest by collectLastValue(underTest.showLabels) - assertThat(latest).isFalse() - - setShowLabelsInSharedPreferences(true) - assertThat(latest).isTrue() - - setShowLabelsInSharedPreferences(false) - assertThat(latest).isFalse() - } - } - - @Test - fun showLabels_updatesFromUserChange() = - with(kosmos) { - testScope.runTest { - fakeUserRepository.setUserInfos(USERS) - val latest by collectLastValue(underTest.showLabels) - - fakeUserRepository.setSelectedUserInfo(PRIMARY_USER) - setShowLabelsInSharedPreferences(false) - - fakeUserRepository.setSelectedUserInfo(ANOTHER_USER) - setShowLabelsInSharedPreferences(true) - - fakeUserRepository.setSelectedUserInfo(PRIMARY_USER) - assertThat(latest).isFalse() - } - } - - @Test - fun setShowLabels_inSharedPreferences() { - underTest.setShowLabels(false) - assertThat(getShowLabelsFromSharedPreferences(true)).isFalse() - - underTest.setShowLabels(true) - assertThat(getShowLabelsFromSharedPreferences(false)).isTrue() - } - - @Test - fun setShowLabels_forDifferentUser() = - with(kosmos) { - testScope.runTest { - fakeUserRepository.setUserInfos(USERS) - - fakeUserRepository.setSelectedUserInfo(PRIMARY_USER) - underTest.setShowLabels(false) - assertThat(getShowLabelsFromSharedPreferences(true)).isFalse() - - fakeUserRepository.setSelectedUserInfo(ANOTHER_USER) - underTest.setShowLabels(true) - assertThat(getShowLabelsFromSharedPreferences(false)).isTrue() - - fakeUserRepository.setSelectedUserInfo(PRIMARY_USER) - assertThat(getShowLabelsFromSharedPreferences(true)).isFalse() - } - } - private fun getSharedPreferences(): SharedPreferences = with(kosmos) { return userFileManager.getSharedPreferences( diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/EditTileListStateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/EditTileListStateTest.kt index 3910903af4aa..ae7c44e9b146 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/EditTileListStateTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/EditTileListStateTest.kt @@ -35,7 +35,7 @@ import org.junit.runner.RunWith @SmallTest @RunWith(AndroidJUnit4::class) class EditTileListStateTest : SysuiTestCase() { - private val underTest = EditTileListState(TestEditTiles, 4) + private val underTest = EditTileListState(TestEditTiles, columns = 4, largeTilesSpan = 2) @Test fun startDrag_listHasSpacers() { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java index 0729e2fcd35f..03c1f92aad4c 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java @@ -93,6 +93,8 @@ public class QuickAccessWalletTileTest extends SysuiTestCase { private static final String CARD_DESCRIPTION = "•••• 1234"; private static final Icon CARD_IMAGE = Icon.createWithBitmap(Bitmap.createBitmap(70, 50, Bitmap.Config.ARGB_8888)); + private static final Icon INVALID_CARD_IMAGE = + Icon.createWithContentUri("content://media/external/images/media"); private static final int PRIMARY_USER_ID = 0; private static final int SECONDARY_USER_ID = 10; @@ -444,6 +446,14 @@ public class QuickAccessWalletTileTest extends SysuiTestCase { } @Test + public void testQueryCards_invalidDrawable_noSideViewDrawable() { + when(mKeyguardStateController.isUnlocked()).thenReturn(true); + setUpInvalidWalletCard(/* hasCard= */ true); + + assertNull(mTile.getState().sideViewCustomDrawable); + } + + @Test public void testQueryCards_error_notUpdateSideViewDrawable() { String errorMessage = "getWalletCardsError"; GetWalletCardsError error = new GetWalletCardsError(CARD_IMAGE, errorMessage); @@ -503,9 +513,33 @@ public class QuickAccessWalletTileTest extends SysuiTestCase { mTestableLooper.processAllMessages(); } + private void setUpInvalidWalletCard(boolean hasCard) { + GetWalletCardsResponse response = + new GetWalletCardsResponse( + hasCard + ? Collections.singletonList(createInvalidWalletCard(mContext)) + : Collections.EMPTY_LIST, 0); + + mTile.handleSetListening(true); + + verify(mController).queryWalletCards(mCallbackCaptor.capture()); + + mCallbackCaptor.getValue().onWalletCardsRetrieved(response); + mTestableLooper.processAllMessages(); + } + private WalletCard createWalletCard(Context context) { PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, mWalletIntent, PendingIntent.FLAG_IMMUTABLE); return new WalletCard.Builder(CARD_ID, CARD_IMAGE, CARD_DESCRIPTION, pendingIntent).build(); } + + private WalletCard createInvalidWalletCard(Context context) { + PendingIntent pendingIntent = + PendingIntent.getActivity(context, 0, mWalletIntent, PendingIntent.FLAG_IMMUTABLE); + return new WalletCard.Builder( + CARD_ID, INVALID_CARD_IMAGE, CARD_DESCRIPTION, pendingIntent).build(); + } + + } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt index 3be8a380b191..b5f005cdc706 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt @@ -18,6 +18,7 @@ package com.android.systemui.scene +import android.provider.Settings import android.telephony.TelephonyManager import android.testing.TestableLooper.RunWithLooper import androidx.test.ext.junit.runners.AndroidJUnit4 @@ -42,6 +43,7 @@ import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.flags.Flags import com.android.systemui.flags.fakeFeatureFlagsClassic +import com.android.systemui.keyguard.KeyguardViewMediator import com.android.systemui.keyguard.ui.viewmodel.lockscreenUserActionsViewModel import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.collectLastValue @@ -64,6 +66,7 @@ import com.android.systemui.telephony.data.repository.fakeTelephonyRepository import com.android.systemui.testKosmos import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.whenever +import com.android.systemui.util.settings.data.repository.userAwareSecureSettingsRepository import com.android.telecom.mockTelecomManager import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertWithMessage @@ -72,6 +75,7 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.launch +import kotlinx.coroutines.test.advanceTimeBy import kotlinx.coroutines.test.runCurrent import org.junit.Before import org.junit.Test @@ -541,7 +545,14 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { .isTrue() powerInteractor.setAsleepForTest() - testScope.runCurrent() + testScope.advanceTimeBy( + kosmos.userAwareSecureSettingsRepository + .getInt( + Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT, + KeyguardViewMediator.KEYGUARD_LOCK_AFTER_DELAY_DEFAULT, + ) + .toLong() + ) powerInteractor.setAwakeForTest() testScope.runCurrent() @@ -631,14 +642,25 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { } /** Changes device wakefulness state from awake to asleep, going through intermediary states. */ - private fun Kosmos.putDeviceToSleep() { + private suspend fun Kosmos.putDeviceToSleep(waitForLock: Boolean = true) { val wakefulnessModel = powerInteractor.detailedWakefulness.value assertWithMessage("Cannot put device to sleep as it's already asleep!") .that(wakefulnessModel.isAwake()) .isTrue() powerInteractor.setAsleepForTest() - testScope.runCurrent() + if (waitForLock) { + testScope.advanceTimeBy( + kosmos.userAwareSecureSettingsRepository + .getInt( + Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT, + KeyguardViewMediator.KEYGUARD_LOCK_AFTER_DELAY_DEFAULT, + ) + .toLong() + ) + } else { + testScope.runCurrent() + } } /** Emulates the dismissal of the IME (soft keyboard). */ 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 55f88cc5b7a2..af0274b1caaa 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 @@ -23,6 +23,7 @@ import android.hardware.face.FaceManager import android.os.PowerManager import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags +import android.provider.Settings import android.view.Display import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest @@ -43,6 +44,7 @@ import com.android.systemui.biometrics.data.repository.fingerprintPropertyReposi import com.android.systemui.biometrics.shared.model.FingerprintSensorType import com.android.systemui.biometrics.shared.model.SensorStrength import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository +import com.android.systemui.bouncer.domain.interactor.alternateBouncerInteractor import com.android.systemui.bouncer.domain.interactor.bouncerInteractor import com.android.systemui.bouncer.shared.logging.BouncerUiEvent import com.android.systemui.classifier.FalsingCollector @@ -53,12 +55,14 @@ import com.android.systemui.coroutines.collectLastValue import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository import com.android.systemui.deviceentry.domain.interactor.deviceEntryHapticsInteractor import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor +import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor import com.android.systemui.deviceentry.shared.model.FailedFaceAuthenticationStatus import com.android.systemui.deviceentry.shared.model.SuccessFaceAuthenticationStatus import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.haptics.msdl.fakeMSDLPlayer import com.android.systemui.haptics.vibratorHelper import com.android.systemui.keyevent.data.repository.fakeKeyEventRepository +import com.android.systemui.keyguard.KeyguardViewMediator import com.android.systemui.keyguard.data.repository.biometricSettingsRepository import com.android.systemui.keyguard.data.repository.deviceEntryFingerprintAuthRepository import com.android.systemui.keyguard.data.repository.fakeBiometricSettingsRepository @@ -106,11 +110,13 @@ import com.android.systemui.statusbar.policy.data.repository.fakeDeviceProvision import com.android.systemui.statusbar.sysuiStatusBarStateController import com.android.systemui.testKosmos import com.android.systemui.util.mockito.mock +import com.android.systemui.util.settings.data.repository.userAwareSecureSettingsRepository import com.google.android.msdl.data.model.MSDLToken import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.map import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.advanceTimeBy import kotlinx.coroutines.test.runCurrent @@ -753,7 +759,7 @@ class SceneContainerStartableTest : SysuiTestCase() { lastSleepReason = WakeSleepReason.POWER_BUTTON, powerButtonLaunchGestureTriggered = false, ) - transitionStateFlow.value = Transition(from = Scenes.Shade, to = Scenes.Lockscreen) + transitionStateFlow.value = Transition(from = Scenes.Gone, to = Scenes.Lockscreen) assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen) kosmos.fakePowerRepository.updateWakefulness( @@ -1339,7 +1345,14 @@ class SceneContainerStartableTest : SysuiTestCase() { // Putting the device to sleep to lock it again, which shouldn't report another // successful unlock. kosmos.powerInteractor.setAsleepForTest() - runCurrent() + advanceTimeBy( + kosmos.userAwareSecureSettingsRepository + .getInt( + Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT, + KeyguardViewMediator.KEYGUARD_LOCK_AFTER_DELAY_DEFAULT, + ) + .toLong() + ) // Verify that the startable changed the scene to Lockscreen because the device locked // following the sleep. assertThat(currentScene).isEqualTo(Scenes.Lockscreen) @@ -2346,6 +2359,58 @@ class SceneContainerStartableTest : SysuiTestCase() { assertThat(isLockscreenEnabled).isTrue() } + @Test + fun replacesLockscreenSceneOnBackStack_whenUnlockdViaAlternateBouncer_fromShade() = + testScope.runTest { + val transitionState = + prepareState( + isDeviceUnlocked = false, + initialSceneKey = Scenes.Lockscreen, + authenticationMethod = AuthenticationMethodModel.Pin, + ) + underTest.start() + + val isUnlocked by + collectLastValue( + kosmos.deviceUnlockedInteractor.deviceUnlockStatus.map { it.isUnlocked } + ) + val currentScene by collectLastValue(sceneInteractor.currentScene) + val backStack by collectLastValue(sceneBackInteractor.backStack) + val isAlternateBouncerVisible by + collectLastValue(kosmos.alternateBouncerInteractor.isVisible) + assertThat(isUnlocked).isFalse() + assertThat(currentScene).isEqualTo(Scenes.Lockscreen) + assertThat(isAlternateBouncerVisible).isFalse() + + // Change to shade. + sceneInteractor.changeScene(Scenes.Shade, "") + transitionState.value = ObservableTransitionState.Idle(Scenes.Shade) + runCurrent() + assertThat(isUnlocked).isFalse() + assertThat(currentScene).isEqualTo(Scenes.Shade) + assertThat(backStack?.asIterable()?.first()).isEqualTo(Scenes.Lockscreen) + assertThat(isAlternateBouncerVisible).isFalse() + + // Show the alternate bouncer. + kosmos.alternateBouncerInteractor.forceShow() + kosmos.sysuiStatusBarStateController.leaveOpen = true // leave shade open + runCurrent() + assertThat(isUnlocked).isFalse() + assertThat(currentScene).isEqualTo(Scenes.Shade) + assertThat(backStack?.asIterable()?.first()).isEqualTo(Scenes.Lockscreen) + assertThat(isAlternateBouncerVisible).isTrue() + + // Trigger a fingerprint unlock. + kosmos.deviceEntryFingerprintAuthRepository.setAuthenticationStatus( + SuccessFingerprintAuthenticationStatus(0, true) + ) + runCurrent() + assertThat(isUnlocked).isTrue() + assertThat(currentScene).isEqualTo(Scenes.Shade) + assertThat(backStack?.asIterable()?.first()).isEqualTo(Scenes.Gone) + assertThat(isAlternateBouncerVisible).isFalse() + } + private fun TestScope.emulateSceneTransition( transitionStateFlow: MutableStateFlow<ObservableTransitionState>, toScene: SceneKey, diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadePositionRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadePositionRepositoryTest.kt new file mode 100644 index 000000000000..a9a5cac6112e --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadePositionRepositoryTest.kt @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.shade.data.repository + +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.kosmos.testScope +import com.android.systemui.statusbar.commandline.commandRegistry +import com.android.systemui.testKosmos +import com.google.common.truth.Truth.assertThat +import java.io.PrintWriter +import java.io.StringWriter +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidJUnit4::class) +class ShadePositionRepositoryTest : SysuiTestCase() { + private val kosmos = testKosmos() + private val testScope = kosmos.testScope + private val commandRegistry = kosmos.commandRegistry + private val pw = PrintWriter(StringWriter()) + + private val underTest = ShadePositionRepositoryImpl(commandRegistry) + + @Before + fun setUp() { + underTest.start() + } + + @Test + fun commandDisplayOverride_updatesDisplayId() = + testScope.runTest { + val displayId by collectLastValue(underTest.displayId) + assertThat(displayId).isEqualTo(Display.DEFAULT_DISPLAY) + + val newDisplayId = 2 + commandRegistry.onShellCommand( + pw, + arrayOf("shade_display_override", newDisplayId.toString()), + ) + + assertThat(displayId).isEqualTo(newDisplayId) + } + + @Test + fun commandShadeDisplayOverride_resetsDisplayId() = + testScope.runTest { + val displayId by collectLastValue(underTest.displayId) + assertThat(displayId).isEqualTo(Display.DEFAULT_DISPLAY) + + val newDisplayId = 2 + commandRegistry.onShellCommand( + pw, + arrayOf("shade_display_override", newDisplayId.toString()), + ) + assertThat(displayId).isEqualTo(newDisplayId) + + commandRegistry.onShellCommand(pw, arrayOf("shade_display_override", "reset")) + assertThat(displayId).isEqualTo(Display.DEFAULT_DISPLAY) + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImplTest.kt index 643acdbb9277..2a3878c17a1b 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImplTest.kt @@ -16,18 +16,22 @@ package com.android.systemui.statusbar.connectivity +import android.content.Context +import android.os.UserHandle import android.os.UserManager -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.filters.SmallTest +import android.platform.test.annotations.EnableFlags import android.testing.TestableLooper.RunWithLooper import androidx.lifecycle.Lifecycle +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.Flags.FLAG_MULTIUSER_WIFI_PICKER_TRACKER_SUPPORT import com.android.systemui.SysuiTestCase import com.android.systemui.settings.UserTracker -import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.capture import com.android.wifitrackerlib.WifiEntry import com.android.wifitrackerlib.WifiPickerTracker import com.google.common.truth.Truth.assertThat +import java.util.concurrent.Executor import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -35,36 +39,28 @@ import org.mockito.ArgumentCaptor import org.mockito.ArgumentMatchers.anyList import org.mockito.Captor import org.mockito.Mock -import org.mockito.Mockito.`when` import org.mockito.Mockito.never +import org.mockito.Mockito.times import org.mockito.Mockito.verify +import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations -import java.util.concurrent.Executor +import org.mockito.kotlin.any +import org.mockito.kotlin.mock @SmallTest @RunWith(AndroidJUnit4::class) @RunWithLooper(setAsMainLooper = true) class AccessPointControllerImplTest : SysuiTestCase() { - @Mock - private lateinit var userManager: UserManager - @Mock - private lateinit var userTracker: UserTracker - @Mock - private lateinit var wifiPickerTrackerFactory: - WifiPickerTrackerFactory - @Mock - private lateinit var wifiPickerTracker: WifiPickerTracker - @Mock - private lateinit var callback: AccessPointController.AccessPointCallback - @Mock - private lateinit var otherCallback: AccessPointController.AccessPointCallback - @Mock - private lateinit var wifiEntryConnected: WifiEntry - @Mock - private lateinit var wifiEntryOther: WifiEntry - @Captor - private lateinit var wifiEntryListCaptor: ArgumentCaptor<List<WifiEntry>> + @Mock private lateinit var userManager: UserManager + @Mock private lateinit var userTracker: UserTracker + @Mock private lateinit var wifiPickerTrackerFactory: WifiPickerTrackerFactory + @Mock private lateinit var wifiPickerTracker: WifiPickerTracker + @Mock private lateinit var callback: AccessPointController.AccessPointCallback + @Mock private lateinit var otherCallback: AccessPointController.AccessPointCallback + @Mock private lateinit var wifiEntryConnected: WifiEntry + @Mock private lateinit var wifiEntryOther: WifiEntry + @Captor private lateinit var wifiEntryListCaptor: ArgumentCaptor<List<WifiEntry>> private val instantExecutor = Executor { it.run() } private lateinit var controller: AccessPointControllerImpl @@ -72,19 +68,21 @@ class AccessPointControllerImplTest : SysuiTestCase() { @Before fun setUp() { MockitoAnnotations.initMocks(this) - `when`(wifiPickerTrackerFactory.create(any(), any(), any())).thenReturn(wifiPickerTracker) + `when`(wifiPickerTrackerFactory.create(any(), any(), any(), any())) + .thenReturn(wifiPickerTracker) `when`(wifiPickerTracker.connectedWifiEntry).thenReturn(wifiEntryConnected) - `when`(wifiPickerTracker.wifiEntries).thenReturn(ArrayList<WifiEntry>().apply { - add(wifiEntryOther) - }) + `when`(wifiPickerTracker.wifiEntries) + .thenReturn(ArrayList<WifiEntry>().apply { add(wifiEntryOther) }) - controller = AccessPointControllerImpl( + controller = + AccessPointControllerImpl( + mContext, userManager, userTracker, instantExecutor, - wifiPickerTrackerFactory - ) + wifiPickerTrackerFactory, + ) controller.init() } @@ -183,13 +181,15 @@ class AccessPointControllerImplTest : SysuiTestCase() { @Test fun testReturnEmptyListWhenNoWifiPickerTracker() { - `when`(wifiPickerTrackerFactory.create(any(), any(), any())).thenReturn(null) - val otherController = AccessPointControllerImpl( + `when`(wifiPickerTrackerFactory.create(any(), any(), any(), any())).thenReturn(null) + val otherController = + AccessPointControllerImpl( + mContext, userManager, userTracker, instantExecutor, - wifiPickerTrackerFactory - ) + wifiPickerTrackerFactory, + ) otherController.init() otherController.addAccessPointCallback(callback) @@ -244,4 +244,19 @@ class AccessPointControllerImplTest : SysuiTestCase() { verify(wifiEntryOther).connect(any()) verify(callback, never()).onSettingsActivityTriggered(any()) } + + @Test + @EnableFlags(FLAG_MULTIUSER_WIFI_PICKER_TRACKER_SUPPORT) + fun switchUsers() { + val primaryUserMockContext = mock<Context>() + mContext.prepareCreateContextAsUser(UserHandle.of(PRIMARY_USER_ID), primaryUserMockContext) + controller.onUserSwitched(PRIMARY_USER_ID) + // Create is expected to be called once when the test starts and a second time when the user + // is switched. + verify(wifiPickerTrackerFactory, times(2)).create(any(), any(), any(), any()) + } + + private companion object { + private const val PRIMARY_USER_ID = 1 + } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarInitializerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarInitializerTest.kt index 938da88a8819..009b33b9f808 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarInitializerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarInitializerTest.kt @@ -23,7 +23,6 @@ import android.platform.test.annotations.EnableFlags import android.view.ViewGroup 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.fragments.FragmentHostManager import com.android.systemui.kosmos.useUnconfinedTestDispatcher @@ -81,21 +80,21 @@ class StatusBarInitializerTest : SysuiTestCase() { ) @Test - @EnableFlags(Flags.FLAG_STATUS_BAR_SIMPLE_FRAGMENT) - fun simpleFragment_startsFromCoreStartable() { + @EnableFlags(StatusBarRootModernization.FLAG_NAME) + fun flagOn_startsFromCoreStartable() { underTest.start() assertThat(underTest.initialized).isTrue() } @Test - @EnableFlags(Flags.FLAG_STATUS_BAR_SIMPLE_FRAGMENT) - fun simpleFragment_throwsIfInitializeIsCalled() { + @EnableFlags(StatusBarRootModernization.FLAG_NAME) + fun flagOn_throwsIfInitializeIsCalled() { assertThrows(IllegalStateException::class.java) { underTest.initializeStatusBar() } } @Test - @EnableFlags(Flags.FLAG_STATUS_BAR_SIMPLE_FRAGMENT) - fun simpleFragment_flagEnabled_doesNotCreateFragment() { + @EnableFlags(StatusBarRootModernization.FLAG_NAME) + fun flagOn_flagEnabled_doesNotCreateFragment() { underTest.start() verify(fragmentManager, never()).beginTransaction() @@ -103,14 +102,14 @@ class StatusBarInitializerTest : SysuiTestCase() { } @Test - @DisableFlags(Flags.FLAG_STATUS_BAR_SIMPLE_FRAGMENT) + @DisableFlags(StatusBarRootModernization.FLAG_NAME) fun flagOff_startCalled_stillInitializes() { underTest.start() assertThat(underTest.initialized).isTrue() } @Test - @DisableFlags(Flags.FLAG_STATUS_BAR_SIMPLE_FRAGMENT) + @DisableFlags(StatusBarRootModernization.FLAG_NAME) fun flagOff_doesNotThrowIfInitializeIsCalled() { underTest.initializeStatusBar() assertThat(underTest.initialized).isTrue() 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 ea5c29ef30aa..3ad41a54ac7e 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 @@ -16,6 +16,8 @@ package com.android.systemui.statusbar.notification.collection.coordinator; +import static com.android.systemui.flags.SceneContainerFlagParameterizationKt.parameterizeSceneContainerFlag; + import static com.google.common.truth.Truth.assertThat; import static junit.framework.Assert.assertFalse; @@ -32,9 +34,9 @@ import static org.mockito.Mockito.when; import static kotlinx.coroutines.flow.StateFlowKt.MutableStateFlow; import android.platform.test.annotations.EnableFlags; +import android.platform.test.flag.junit.FlagsParameterization; import android.testing.TestableLooper; -import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.compose.animation.scene.ObservableTransitionState; @@ -42,6 +44,7 @@ import com.android.systemui.Flags; import com.android.systemui.SysuiTestCase; import com.android.systemui.communal.shared.model.CommunalScenes; import com.android.systemui.dump.DumpManager; +import com.android.systemui.flags.BrokenWithSceneContainer; import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.keyguard.shared.model.KeyguardState; import com.android.systemui.keyguard.shared.model.TransitionState; @@ -78,14 +81,23 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.mockito.verification.VerificationMode; +import java.util.List; + import kotlinx.coroutines.flow.MutableStateFlow; import kotlinx.coroutines.test.TestScope; +import platform.test.runner.parameterized.ParameterizedAndroidJunit4; +import platform.test.runner.parameterized.Parameters; @SmallTest -@RunWith(AndroidJUnit4.class) +@RunWith(ParameterizedAndroidJunit4.class) @TestableLooper.RunWithLooper public class VisualStabilityCoordinatorTest extends SysuiTestCase { + @Parameters(name = "{0}") + public static List<FlagsParameterization> getParams() { + return parameterizeSceneContainerFlag(); + } + private VisualStabilityCoordinator mCoordinator; @Mock private DumpManager mDumpManager; @@ -117,6 +129,11 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase { private NotificationEntry mEntry; private GroupEntry mGroupEntry; + public VisualStabilityCoordinatorTest(FlagsParameterization flags) { + super(); + mSetFlagsRule.setFlagsParameterization(flags); + } + @Before public void setUp() { MockitoAnnotations.initMocks(this); @@ -251,6 +268,7 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase { } @Test + @BrokenWithSceneContainer(bugId = 377868472) // mReorderingAllowed is broken with SceneContainer public void testLockscreenPartlyShowing_groupAndSectionChangesNotAllowed() { // GIVEN the panel true expanded and device isn't pulsing setFullyDozed(false); @@ -267,6 +285,7 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase { } @Test + @BrokenWithSceneContainer(bugId = 377868472) // mReorderingAllowed is broken with SceneContainer public void testLockscreenFullyShowing_groupAndSectionChangesNotAllowed() { // GIVEN the panel true expanded and device isn't pulsing setFullyDozed(false); @@ -520,6 +539,7 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase { @Test @EnableFlags(Flags.FLAG_CHECK_LOCKSCREEN_GONE_TRANSITION) + @BrokenWithSceneContainer(bugId = 377868472) // mReorderingAllowed is broken with SceneContainer public void testNotLockscreenInGoneTransition_invalidationCalled() { // GIVEN visual stability is being maintained b/c animation is playing mKosmos.getKeyguardTransitionRepository().sendTransitionStepJava( @@ -589,6 +609,7 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase { } @Test + @BrokenWithSceneContainer(bugId = 377868472) // mReorderingAllowed is broken with SceneContainer public void testCommunalShowingWillNotSuppressReordering() { // GIVEN panel is expanded, communal is showing, and QS is collapsed setPulsing(false); diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt index dae5542123ed..50db9f7268e4 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt @@ -21,16 +21,19 @@ import android.service.notification.StatusBarNotification import android.view.View.VISIBLE import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.systemui.res.R import com.android.systemui.SysuiTestCase +import com.android.systemui.kosmos.testScope import com.android.systemui.media.controls.domain.pipeline.MediaDataManager +import com.android.systemui.res.R import com.android.systemui.statusbar.LockscreenShadeTransitionController import com.android.systemui.statusbar.StatusBarState import com.android.systemui.statusbar.SysuiStatusBarStateController import com.android.systemui.statusbar.notification.collection.NotificationEntry +import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.ExpandableView import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController +import com.android.systemui.testKosmos import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.eq import com.android.systemui.util.mockito.nullable @@ -52,8 +55,11 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() { private lateinit var lockscreenShadeTransitionController: LockscreenShadeTransitionController @Mock private lateinit var mediaDataManager: MediaDataManager @Mock private lateinit var stackLayout: NotificationStackScrollLayout + @Mock private lateinit var seenNotificationsInteractor: SeenNotificationsInteractor private val testableResources = mContext.orCreateTestableResources + private val kosmos = testKosmos() + private val testScope = kosmos.testScope private lateinit var sizeCalculator: NotificationStackSizeCalculator @@ -72,7 +78,9 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() { lockscreenShadeTransitionController = lockscreenShadeTransitionController, mediaDataManager = mediaDataManager, testableResources.resources, - ResourcesSplitShadeStateController() + ResourcesSplitShadeStateController(), + seenNotificationsInteractor = seenNotificationsInteractor, + scope = testScope, ) } @@ -85,7 +93,7 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() { rows, spaceForNotifications = 0f, spaceForShelf = 0f, - shelfHeight = 0f + shelfHeight = 0f, ) assertThat(maxNotifications).isEqualTo(0) @@ -101,7 +109,7 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() { rows, spaceForNotifications = Float.MAX_VALUE, spaceForShelf = Float.MAX_VALUE, - shelfHeight + shelfHeight, ) assertThat(maxNotifications).isEqualTo(numberOfRows) @@ -137,7 +145,7 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() { listOf(row), /* spaceForNotifications= */ 5f, /* spaceForShelf= */ 0f, - /* shelfHeight= */ 0f + /* shelfHeight= */ 0f, ) assertThat(maxNotifications).isEqualTo(1) @@ -148,11 +156,7 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() { setGapHeight(gapHeight) val shelfHeight = shelfHeight + dividerHeight val spaceForNotifications = - listOf( - rowHeight + dividerHeight, - gapHeight + rowHeight + dividerHeight, - ) - .sum() + listOf(rowHeight + dividerHeight, gapHeight + rowHeight + dividerHeight).sum() val spaceForShelf = gapHeight + dividerHeight + shelfHeight val rows = listOf(createMockRow(rowHeight), createMockRow(rowHeight), createMockRow(rowHeight)) @@ -162,7 +166,7 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() { rows, spaceForNotifications + 1, spaceForShelf, - shelfHeight + shelfHeight, ) assertThat(maxNotifications).isEqualTo(2) @@ -173,12 +177,7 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() { // Each row in separate section. setGapHeight(gapHeight) - val notifSpace = - listOf( - rowHeight, - dividerHeight + gapHeight + rowHeight, - ) - .sum() + val notifSpace = listOf(rowHeight, dividerHeight + gapHeight + rowHeight).sum() val shelfSpace = dividerHeight + gapHeight + shelfHeight val spaceUsed = notifSpace + shelfSpace @@ -209,7 +208,7 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() { rows, spaceForNotifications + 1, spaceForShelf, - shelfHeight + shelfHeight, ) assertThat(maxNotifications).isEqualTo(1) @@ -252,7 +251,7 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() { visibleIndex = 0, previousView = null, stack = stackLayout, - onLockscreen = true + onLockscreen = true, ) assertThat(space.whenEnoughSpace).isEqualTo(10f) } @@ -272,7 +271,7 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() { visibleIndex = 0, previousView = null, stack = stackLayout, - onLockscreen = true + onLockscreen = true, ) assertThat(space.whenEnoughSpace).isEqualTo(5) } @@ -291,7 +290,7 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() { visibleIndex = 0, previousView = null, stack = stackLayout, - onLockscreen = true + onLockscreen = true, ) assertThat(space.whenSavingSpace).isEqualTo(5) } @@ -311,7 +310,7 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() { visibleIndex = 0, previousView = null, stack = stackLayout, - onLockscreen = true + onLockscreen = true, ) assertThat(space.whenSavingSpace).isEqualTo(5) } @@ -330,7 +329,7 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() { visibleIndex = 0, previousView = null, stack = stackLayout, - onLockscreen = false + onLockscreen = false, ) assertThat(space.whenEnoughSpace).isEqualTo(rowHeight) assertThat(space.whenSavingSpace).isEqualTo(rowHeight) @@ -340,14 +339,14 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() { rows: List<ExpandableView>, spaceForNotifications: Float, spaceForShelf: Float, - shelfHeight: Float = this.shelfHeight + shelfHeight: Float = this.shelfHeight, ): Int { setupChildren(rows) return sizeCalculator.computeMaxKeyguardNotifications( stackLayout, spaceForNotifications, spaceForShelf, - shelfHeight + shelfHeight, ) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt index a862fdfeca22..778e8228ab9b 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt @@ -19,6 +19,8 @@ package com.android.systemui.statusbar.phone import android.content.Context import android.content.res.Configuration import android.graphics.Rect +import android.platform.test.annotations.DisableFlags +import android.platform.test.annotations.EnableFlags import android.view.Display import android.view.DisplayCutout import androidx.test.ext.junit.runners.AndroidJUnit4 @@ -29,6 +31,7 @@ import com.android.systemui.SysUICutoutProvider import com.android.systemui.SysuiTestCase import com.android.systemui.dump.DumpManager import com.android.systemui.statusbar.commandline.CommandRegistry +import com.android.systemui.statusbar.core.StatusBarConnectedDisplays import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.util.leak.RotationUtils import com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE @@ -1051,7 +1054,8 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { } @Test - fun onMaxBoundsChanged_beforeStart_listenerNotNotified() { + @EnableFlags(StatusBarConnectedDisplays.FLAG_NAME) + fun onMaxBoundsChanged_beforeStart_flagEnabled_listenerNotNotified() { // Start out with an existing configuration with bounds configuration.windowConfiguration.setMaxBounds(0, 0, 100, 100) configurationController.onConfigurationChanged(configuration) @@ -1083,7 +1087,41 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { } @Test - fun onDensityOrFontScaleChanged_beforeStart_listenerNotNotified() { + @DisableFlags(StatusBarConnectedDisplays.FLAG_NAME) + fun onMaxBoundsChanged_beforeStart_flagDisabled_listenerNotNotified() { + // Start out with an existing configuration with bounds + configuration.windowConfiguration.setMaxBounds(0, 0, 100, 100) + configurationController.onConfigurationChanged(configuration) + val provider = + StatusBarContentInsetsProviderImpl( + contextMock, + configurationController, + mock<DumpManager>(), + mock<CommandRegistry>(), + mock<SysUICutoutProvider>(), + ) + val listener = + object : StatusBarContentInsetsChangedListener { + var triggered = false + + override fun onStatusBarContentInsetsChanged() { + triggered = true + } + } + provider.addCallback(listener) + + // WHEN the config is updated with new bounds + // but provider is not started + configuration.windowConfiguration.setMaxBounds(0, 0, 456, 789) + configurationController.onConfigurationChanged(configuration) + + // THEN the listener is notified + assertThat(listener.triggered).isTrue() + } + + @Test + @EnableFlags(StatusBarConnectedDisplays.FLAG_NAME) + fun onDensityOrFontScaleChanged_beforeStart_flagEnabled_listenerNotNotified() { configuration.densityDpi = 12 val provider = StatusBarContentInsetsProviderImpl( @@ -1112,6 +1150,36 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { } @Test + @DisableFlags(StatusBarConnectedDisplays.FLAG_NAME) + fun onDensityOrFontScaleChanged_beforeStart_flagDisabled_listenerNotified() { + configuration.densityDpi = 12 + val provider = + StatusBarContentInsetsProviderImpl( + contextMock, + configurationController, + mock<DumpManager>(), + mock<CommandRegistry>(), + mock<SysUICutoutProvider>(), + ) + val listener = + object : StatusBarContentInsetsChangedListener { + var triggered = false + + override fun onStatusBarContentInsetsChanged() { + triggered = true + } + } + provider.addCallback(listener) + + // WHEN the config is updated, but the provider is not started + configuration.densityDpi = 20 + configurationController.onConfigurationChanged(configuration) + + // THEN the listener is notified + assertThat(listener.triggered).isTrue() + } + + @Test fun onDensityOrFontScaleChanged_afterStart_listenerNotified() { configuration.densityDpi = 12 val provider = @@ -1169,7 +1237,8 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { } @Test - fun onThemeChanged_beforeStart_listenerNotNotified() { + @EnableFlags(StatusBarConnectedDisplays.FLAG_NAME) + fun onThemeChanged_beforeStart_flagEnabled_listenerNotNotified() { val provider = StatusBarContentInsetsProviderImpl( contextMock, @@ -1193,6 +1262,32 @@ class StatusBarContentInsetsProviderTest : SysuiTestCase() { assertThat(listener.triggered).isFalse() } + @Test + @DisableFlags(StatusBarConnectedDisplays.FLAG_NAME) + fun onThemeChanged_beforeStart_flagDisabled_listenerNotified() { + val provider = + StatusBarContentInsetsProviderImpl( + contextMock, + configurationController, + mock<DumpManager>(), + mock<CommandRegistry>(), + mock<SysUICutoutProvider>(), + ) + val listener = + object : StatusBarContentInsetsChangedListener { + var triggered = false + + override fun onStatusBarContentInsetsChanged() { + triggered = true + } + } + provider.addCallback(listener) + + configurationController.notifyThemeChanged() + + assertThat(listener.triggered).isTrue() + } + private fun assertRects( expected: Rect, actual: Rect, diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt index a8bcfbcfc539..39a1c106cfcf 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.pipeline.mobile.domain.interactor +import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import android.telephony.CellSignalStrength import android.telephony.TelephonyManager.NETWORK_TYPE_UNKNOWN @@ -735,9 +736,10 @@ class MobileIconInteractorTest : SysuiTestCase() { } @EnableFlags(com.android.internal.telephony.flags.Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG) + @DisableFlags(com.android.internal.telephony.flags.Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN) @Test // See b/346904529 for more context - fun satBasedIcon_doesNotInflateSignalStrength() = + fun satBasedIcon_doesNotInflateSignalStrength_flagOff() = testScope.runTest { val latest by collectLastValue(underTest.signalLevelIcon) @@ -756,7 +758,75 @@ class MobileIconInteractorTest : SysuiTestCase() { assertThat(latest!!.level).isEqualTo(4) } + @EnableFlags( + com.android.internal.telephony.flags.Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG, + com.android.internal.telephony.flags.Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN, + ) + @Test + // See b/346904529 for more context + fun satBasedIcon_doesNotInflateSignalStrength_flagOn() = + testScope.runTest { + val latest by collectLastValue(underTest.signalLevelIcon) + + // GIVEN a satellite connection + connectionRepository.isNonTerrestrial.value = true + // GIVEN this carrier has set INFLATE_SIGNAL_STRENGTH + connectionRepository.inflateSignalStrength.value = true + + connectionRepository.satelliteLevel.value = 4 + assertThat(latest!!.level).isEqualTo(4) + + connectionRepository.inflateSignalStrength.value = true + connectionRepository.primaryLevel.value = 4 + + // Icon level is unaffected + assertThat(latest!!.level).isEqualTo(4) + } + + @EnableFlags(com.android.internal.telephony.flags.Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG) + @DisableFlags(com.android.internal.telephony.flags.Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN) + @Test + fun satBasedIcon_usesPrimaryLevel_flagOff() = + testScope.runTest { + val latest by collectLastValue(underTest.signalLevelIcon) + + // GIVEN a satellite connection + connectionRepository.isNonTerrestrial.value = true + + // GIVEN primary level is set + connectionRepository.primaryLevel.value = 4 + connectionRepository.satelliteLevel.value = 0 + + // THEN icon uses the primary level because the flag is off + assertThat(latest!!.level).isEqualTo(4) + } + + @EnableFlags( + com.android.internal.telephony.flags.Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG, + com.android.internal.telephony.flags.Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN, + ) + @Test + fun satBasedIcon_usesSatelliteLevel_flagOn() = + testScope.runTest { + val latest by collectLastValue(underTest.signalLevelIcon) + + // GIVEN a satellite connection + connectionRepository.isNonTerrestrial.value = true + + // GIVEN satellite level is set + connectionRepository.satelliteLevel.value = 4 + connectionRepository.primaryLevel.value = 0 + + // THEN icon uses the satellite level because the flag is on + assertThat(latest!!.level).isEqualTo(4) + } + + /** + * Context (b/377518113), this test will not be needed after FLAG_CARRIER_ROAMING_NB_IOT_NTN is + * rolled out. The new API should report 0 automatically if not in service. + */ @EnableFlags(com.android.internal.telephony.flags.Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG) + @DisableFlags(com.android.internal.telephony.flags.Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN) @Test fun satBasedIcon_reportsLevelZeroWhenOutOfService() = testScope.runTest { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt index 4c7cdfa7fb67..038722cd9608 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt @@ -151,7 +151,7 @@ class MobileIconViewModelTest : SysuiTestCase() { iconsInteractor.isForceHidden, repository, context, - MobileIconCarrierIdOverridesFake() + MobileIconCarrierIdOverridesFake(), ) createAndSetViewModel() } @@ -359,7 +359,7 @@ class MobileIconViewModelTest : SysuiTestCase() { val expected = Icon.Resource( THREE_G.dataType, - ContentDescription.Resource(THREE_G.dataContentDescription) + ContentDescription.Resource(THREE_G.dataContentDescription), ) connectionsRepository.mobileIsDefault.value = true repository.setNetworkTypeKey(connectionsRepository.GSM_KEY) @@ -406,7 +406,7 @@ class MobileIconViewModelTest : SysuiTestCase() { val expected = Icon.Resource( THREE_G.dataType, - ContentDescription.Resource(THREE_G.dataContentDescription) + ContentDescription.Resource(THREE_G.dataContentDescription), ) repository.setNetworkTypeKey(connectionsRepository.GSM_KEY) repository.setDataEnabled(true) @@ -426,7 +426,7 @@ class MobileIconViewModelTest : SysuiTestCase() { val initial = Icon.Resource( THREE_G.dataType, - ContentDescription.Resource(THREE_G.dataContentDescription) + ContentDescription.Resource(THREE_G.dataContentDescription), ) repository.setNetworkTypeKey(connectionsRepository.GSM_KEY) @@ -448,7 +448,7 @@ class MobileIconViewModelTest : SysuiTestCase() { val expected = Icon.Resource( THREE_G.dataType, - ContentDescription.Resource(THREE_G.dataContentDescription) + ContentDescription.Resource(THREE_G.dataContentDescription), ) repository.dataEnabled.value = true var latest: Icon? = null @@ -477,7 +477,7 @@ class MobileIconViewModelTest : SysuiTestCase() { val expected = Icon.Resource( THREE_G.dataType, - ContentDescription.Resource(THREE_G.dataContentDescription) + ContentDescription.Resource(THREE_G.dataContentDescription), ) assertThat(latest).isEqualTo(expected) @@ -499,7 +499,7 @@ class MobileIconViewModelTest : SysuiTestCase() { val expected = Icon.Resource( THREE_G.dataType, - ContentDescription.Resource(THREE_G.dataContentDescription) + ContentDescription.Resource(THREE_G.dataContentDescription), ) assertThat(latest).isEqualTo(expected) @@ -520,7 +520,7 @@ class MobileIconViewModelTest : SysuiTestCase() { val expected = Icon.Resource( THREE_G.dataType, - ContentDescription.Resource(THREE_G.dataContentDescription) + ContentDescription.Resource(THREE_G.dataContentDescription), ) assertThat(latest).isEqualTo(expected) @@ -542,7 +542,7 @@ class MobileIconViewModelTest : SysuiTestCase() { val expected = Icon.Resource( connectionsRepository.defaultMobileIconGroup.value.dataType, - ContentDescription.Resource(G.dataContentDescription) + ContentDescription.Resource(G.dataContentDescription), ) assertThat(latest).isEqualTo(expected) @@ -564,7 +564,7 @@ class MobileIconViewModelTest : SysuiTestCase() { val expected = Icon.Resource( THREE_G.dataType, - ContentDescription.Resource(THREE_G.dataContentDescription) + ContentDescription.Resource(THREE_G.dataContentDescription), ) assertThat(latest).isEqualTo(expected) @@ -621,10 +621,7 @@ class MobileIconViewModelTest : SysuiTestCase() { underTest.activityInVisible.onEach { containerVisible = it }.launchIn(this) repository.dataActivityDirection.value = - DataActivityModel( - hasActivityIn = true, - hasActivityOut = true, - ) + DataActivityModel(hasActivityIn = true, hasActivityOut = true) assertThat(inVisible).isFalse() assertThat(outVisible).isFalse() @@ -654,10 +651,7 @@ class MobileIconViewModelTest : SysuiTestCase() { underTest.activityContainerVisible.onEach { containerVisible = it }.launchIn(this) repository.dataActivityDirection.value = - DataActivityModel( - hasActivityIn = true, - hasActivityOut = false, - ) + DataActivityModel(hasActivityIn = true, hasActivityOut = false) yield() @@ -666,20 +660,14 @@ class MobileIconViewModelTest : SysuiTestCase() { assertThat(containerVisible).isTrue() repository.dataActivityDirection.value = - DataActivityModel( - hasActivityIn = false, - hasActivityOut = true, - ) + DataActivityModel(hasActivityIn = false, hasActivityOut = true) assertThat(inVisible).isFalse() assertThat(outVisible).isTrue() assertThat(containerVisible).isTrue() repository.dataActivityDirection.value = - DataActivityModel( - hasActivityIn = false, - hasActivityOut = false, - ) + DataActivityModel(hasActivityIn = false, hasActivityOut = false) assertThat(inVisible).isFalse() assertThat(outVisible).isFalse() @@ -709,10 +697,7 @@ class MobileIconViewModelTest : SysuiTestCase() { underTest.activityContainerVisible.onEach { containerVisible = it }.launchIn(this) repository.dataActivityDirection.value = - DataActivityModel( - hasActivityIn = true, - hasActivityOut = false, - ) + DataActivityModel(hasActivityIn = true, hasActivityOut = false) yield() @@ -721,20 +706,14 @@ class MobileIconViewModelTest : SysuiTestCase() { assertThat(containerVisible).isTrue() repository.dataActivityDirection.value = - DataActivityModel( - hasActivityIn = false, - hasActivityOut = true, - ) + DataActivityModel(hasActivityIn = false, hasActivityOut = true) assertThat(inVisible).isFalse() assertThat(outVisible).isTrue() assertThat(containerVisible).isTrue() repository.dataActivityDirection.value = - DataActivityModel( - hasActivityIn = false, - hasActivityOut = false, - ) + DataActivityModel(hasActivityIn = false, hasActivityOut = false) assertThat(inVisible).isFalse() assertThat(outVisible).isFalse() @@ -824,10 +803,7 @@ class MobileIconViewModelTest : SysuiTestCase() { // sets the background on cellular repository.hasPrioritizedNetworkCapabilities.value = true repository.dataActivityDirection.value = - DataActivityModel( - hasActivityIn = true, - hasActivityOut = true, - ) + DataActivityModel(hasActivityIn = true, hasActivityOut = true) assertThat(roaming).isFalse() assertThat(networkTypeIcon).isNull() @@ -838,11 +814,13 @@ class MobileIconViewModelTest : SysuiTestCase() { } @EnableFlags(com.android.internal.telephony.flags.Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG) + @DisableFlags(com.android.internal.telephony.flags.Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN) @Test - fun nonTerrestrial_usesSatelliteIcon() = + fun nonTerrestrial_usesSatelliteIcon_flagOff() = testScope.runTest { repository.isNonTerrestrial.value = true repository.setAllLevels(0) + repository.satelliteLevel.value = 0 val latest by collectLastValue(underTest.icon.filterIsInstance(SignalIconModel.Satellite::class)) @@ -853,28 +831,66 @@ class MobileIconViewModelTest : SysuiTestCase() { // 1-2 -> 1 bar repository.setAllLevels(1) + repository.satelliteLevel.value = 1 assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_1) repository.setAllLevels(2) + repository.satelliteLevel.value = 2 assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_1) // 3-4 -> 2 bars repository.setAllLevels(3) + repository.satelliteLevel.value = 3 assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_2) repository.setAllLevels(4) + repository.satelliteLevel.value = 4 + assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_2) + } + + @EnableFlags( + com.android.internal.telephony.flags.Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG, + com.android.internal.telephony.flags.Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN, + ) + @Test + fun nonTerrestrial_usesSatelliteIcon_flagOn() = + testScope.runTest { + repository.isNonTerrestrial.value = true + repository.satelliteLevel.value = 0 + + val latest by + collectLastValue(underTest.icon.filterIsInstance(SignalIconModel.Satellite::class)) + + // Level 0 -> no connection + assertThat(latest).isNotNull() + assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_0) + + // 1-2 -> 1 bar + repository.satelliteLevel.value = 1 + assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_1) + + repository.satelliteLevel.value = 2 + assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_1) + + // 3-4 -> 2 bars + repository.satelliteLevel.value = 3 + assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_2) + + repository.satelliteLevel.value = 4 assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_2) } @EnableFlags(com.android.internal.telephony.flags.Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG) + @DisableFlags(com.android.internal.telephony.flags.Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN) @Test - fun satelliteIcon_ignoresInflateSignalStrength() = + fun satelliteIcon_ignoresInflateSignalStrength_flagOff() = testScope.runTest { // Note that this is the exact same test as above, but with inflateSignalStrength set to // true we note that the level is unaffected by inflation repository.inflateSignalStrength.value = true repository.isNonTerrestrial.value = true repository.setAllLevels(0) + repository.satelliteLevel.value = 0 val latest by collectLastValue(underTest.icon.filterIsInstance(SignalIconModel.Satellite::class)) @@ -885,16 +901,55 @@ class MobileIconViewModelTest : SysuiTestCase() { // 1-2 -> 1 bar repository.setAllLevels(1) + repository.satelliteLevel.value = 1 assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_1) repository.setAllLevels(2) + repository.satelliteLevel.value = 2 assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_1) // 3-4 -> 2 bars repository.setAllLevels(3) + repository.satelliteLevel.value = 3 assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_2) repository.setAllLevels(4) + repository.satelliteLevel.value = 4 + assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_2) + } + + @EnableFlags( + com.android.internal.telephony.flags.Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG, + com.android.internal.telephony.flags.Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN, + ) + @Test + fun satelliteIcon_ignoresInflateSignalStrength_flagOn() = + testScope.runTest { + // Note that this is the exact same test as above, but with inflateSignalStrength set to + // true we note that the level is unaffected by inflation + repository.inflateSignalStrength.value = true + repository.isNonTerrestrial.value = true + repository.satelliteLevel.value = 0 + + val latest by + collectLastValue(underTest.icon.filterIsInstance(SignalIconModel.Satellite::class)) + + // Level 0 -> no connection + assertThat(latest).isNotNull() + assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_0) + + // 1-2 -> 1 bar + repository.satelliteLevel.value = 1 + assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_1) + + repository.satelliteLevel.value = 2 + assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_1) + + // 3-4 -> 2 bars + repository.satelliteLevel.value = 3 + assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_2) + + repository.satelliteLevel.value = 4 assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_2) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewBinder.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewBinder.kt index cdc7aa2dea2a..9888574071e6 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewBinder.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewBinder.kt @@ -32,6 +32,8 @@ class FakeHomeStatusBarViewBinder : HomeStatusBarViewBinder { override fun bind( view: View, viewModel: HomeStatusBarViewModel, + systemEventChipAnimateIn: ((View) -> Unit)?, + systemEventChipAnimateOut: ((View) -> Unit)?, listener: StatusBarVisibilityChangeListener, ) { this.listener = listener diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt index 02c1540d3d8b..eef5753cef8a 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt @@ -19,6 +19,7 @@ package com.android.systemui.statusbar.pipeline.shared.ui.viewmodel import android.view.View import com.android.systemui.statusbar.chips.ui.model.MultipleOngoingActivityChipsModel import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel +import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState.Idle import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow @@ -53,11 +54,14 @@ class FakeHomeStatusBarViewModel : HomeStatusBarViewModel { ) ) - override val isSystemInfoVisible = + override val systemInfoCombinedVis = MutableStateFlow( - HomeStatusBarViewModel.VisibilityModel( - visibility = View.GONE, - shouldAnimateChange = false, + HomeStatusBarViewModel.SystemInfoCombinedVisibilityModel( + HomeStatusBarViewModel.VisibilityModel( + visibility = View.GONE, + shouldAnimateChange = false, + ), + Idle, ) ) 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 b3a73d82122f..c4d2569bba89 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 @@ -60,11 +60,16 @@ import com.android.systemui.statusbar.data.repository.FakeStatusBarModeRepositor import com.android.systemui.statusbar.data.repository.fakeStatusBarModeRepository import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel import com.android.systemui.statusbar.disableflags.data.repository.fakeDisableFlagsRepository +import com.android.systemui.statusbar.events.data.repository.systemStatusEventAnimationRepository +import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState.AnimatingIn +import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState.AnimatingOut +import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState.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.activeNotificationListRepository import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor +import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.HomeStatusBarViewModel.VisibilityModel import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.emptyFlow @@ -90,6 +95,7 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { 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 @@ -546,25 +552,50 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { @Test fun isSystemInfoVisible_allowedByDisableFlags_visible() = testScope.runTest { - val latest by collectLastValue(underTest.isSystemInfoVisible) + val latest by collectLastValue(underTest.systemInfoCombinedVis) transitionKeyguardToGone() disableFlagsRepository.disableFlags.value = DisableFlagsModel(DISABLE_NONE, DISABLE2_NONE) - assertThat(latest!!.visibility).isEqualTo(View.VISIBLE) + assertThat(latest!!.baseVisibility.visibility).isEqualTo(View.VISIBLE) } @Test fun isSystemInfoVisible_notAllowedByDisableFlags_gone() = testScope.runTest { - val latest by collectLastValue(underTest.isSystemInfoVisible) + val latest by collectLastValue(underTest.systemInfoCombinedVis) transitionKeyguardToGone() disableFlagsRepository.disableFlags.value = DisableFlagsModel(DISABLE_SYSTEM_INFO, DISABLE2_NONE) - assertThat(latest!!.visibility).isEqualTo(View.GONE) + assertThat(latest!!.baseVisibility.visibility).isEqualTo(View.GONE) + } + + @Test + fun systemInfoCombineVis_animationsPassThrough() = + testScope.runTest { + val latest by collectLastValue(underTest.systemInfoCombinedVis) + transitionKeyguardToGone() + + assertThat(latest!!.baseVisibility) + .isEqualTo(VisibilityModel(visibility = View.VISIBLE, shouldAnimateChange = false)) + assertThat(latest!!.animationState).isEqualTo(Idle) + + // WHEN the animation state changes, but the visibility state doesn't change + systemStatusEventAnimationRepository.animationState.value = AnimatingIn + + // THEN the visibility is the same + assertThat(latest!!.baseVisibility) + .isEqualTo(VisibilityModel(visibility = View.VISIBLE, shouldAnimateChange = false)) + // THEN the animation state updates + assertThat(latest!!.animationState).isEqualTo(AnimatingIn) + + systemStatusEventAnimationRepository.animationState.value = AnimatingOut + assertThat(latest!!.baseVisibility) + .isEqualTo(VisibilityModel(visibility = View.VISIBLE, shouldAnimateChange = false)) + assertThat(latest!!.animationState).isEqualTo(AnimatingOut) } @Test @@ -573,7 +604,7 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { testScope.runTest { val clockVisible by collectLastValue(underTest.isClockVisible) val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible) - val systemInfoVisible by collectLastValue(underTest.isSystemInfoVisible) + val systemInfoVisible by collectLastValue(underTest.systemInfoCombinedVis) keyguardTransitionRepository.sendTransitionSteps( from = KeyguardState.GONE, @@ -583,7 +614,7 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { assertThat(clockVisible!!.visibility).isEqualTo(View.GONE) assertThat(notifIconsVisible!!.visibility).isEqualTo(View.GONE) - assertThat(systemInfoVisible!!.visibility).isEqualTo(View.GONE) + assertThat(systemInfoVisible!!.baseVisibility.visibility).isEqualTo(View.GONE) } @Test @@ -592,13 +623,13 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { testScope.runTest { val clockVisible by collectLastValue(underTest.isClockVisible) val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible) - val systemInfoVisible by collectLastValue(underTest.isSystemInfoVisible) + val systemInfoVisible by collectLastValue(underTest.systemInfoCombinedVis) kosmos.sceneContainerRepository.snapToScene(Scenes.Lockscreen) assertThat(clockVisible!!.visibility).isEqualTo(View.GONE) assertThat(notifIconsVisible!!.visibility).isEqualTo(View.GONE) - assertThat(systemInfoVisible!!.visibility).isEqualTo(View.GONE) + assertThat(systemInfoVisible!!.baseVisibility.visibility).isEqualTo(View.GONE) } @Test @@ -607,7 +638,7 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { testScope.runTest { val clockVisible by collectLastValue(underTest.isClockVisible) val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible) - val systemInfoVisible by collectLastValue(underTest.isSystemInfoVisible) + val systemInfoVisible by collectLastValue(underTest.systemInfoCombinedVis) keyguardTransitionRepository.sendTransitionSteps( from = KeyguardState.LOCKSCREEN, @@ -617,7 +648,7 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { assertThat(clockVisible!!.visibility).isEqualTo(View.GONE) assertThat(notifIconsVisible!!.visibility).isEqualTo(View.GONE) - assertThat(systemInfoVisible!!.visibility).isEqualTo(View.GONE) + assertThat(systemInfoVisible!!.baseVisibility.visibility).isEqualTo(View.GONE) } @Test @@ -626,13 +657,13 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { testScope.runTest { val clockVisible by collectLastValue(underTest.isClockVisible) val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible) - val systemInfoVisible by collectLastValue(underTest.isSystemInfoVisible) + val systemInfoVisible by collectLastValue(underTest.systemInfoCombinedVis) kosmos.sceneContainerRepository.snapToScene(Scenes.Bouncer) assertThat(clockVisible!!.visibility).isEqualTo(View.GONE) assertThat(notifIconsVisible!!.visibility).isEqualTo(View.GONE) - assertThat(systemInfoVisible!!.visibility).isEqualTo(View.GONE) + assertThat(systemInfoVisible!!.baseVisibility.visibility).isEqualTo(View.GONE) } @Test @@ -641,7 +672,7 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { testScope.runTest { val clockVisible by collectLastValue(underTest.isClockVisible) val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible) - val systemInfoVisible by collectLastValue(underTest.isSystemInfoVisible) + val systemInfoVisible by collectLastValue(underTest.systemInfoCombinedVis) keyguardTransitionRepository.sendTransitionSteps( from = KeyguardState.LOCKSCREEN, @@ -651,7 +682,7 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { assertThat(clockVisible!!.visibility).isEqualTo(View.VISIBLE) assertThat(notifIconsVisible!!.visibility).isEqualTo(View.VISIBLE) - assertThat(systemInfoVisible!!.visibility).isEqualTo(View.VISIBLE) + assertThat(systemInfoVisible!!.baseVisibility.visibility).isEqualTo(View.VISIBLE) } @Test @@ -660,14 +691,14 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { testScope.runTest { val clockVisible by collectLastValue(underTest.isClockVisible) val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible) - val systemInfoVisible by collectLastValue(underTest.isSystemInfoVisible) + val systemInfoVisible by collectLastValue(underTest.systemInfoCombinedVis) kosmos.sceneContainerRepository.snapToScene(Scenes.Lockscreen) kosmos.keyguardOcclusionRepository.setShowWhenLockedActivityInfo(true, taskInfo = null) assertThat(clockVisible!!.visibility).isEqualTo(View.VISIBLE) assertThat(notifIconsVisible!!.visibility).isEqualTo(View.VISIBLE) - assertThat(systemInfoVisible!!.visibility).isEqualTo(View.VISIBLE) + assertThat(systemInfoVisible!!.baseVisibility.visibility).isEqualTo(View.VISIBLE) } @Test @@ -676,13 +707,13 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { testScope.runTest { val clockVisible by collectLastValue(underTest.isClockVisible) val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible) - val systemInfoVisible by collectLastValue(underTest.isSystemInfoVisible) + val systemInfoVisible by collectLastValue(underTest.systemInfoCombinedVis) transitionKeyguardToGone() assertThat(clockVisible!!.visibility).isEqualTo(View.VISIBLE) assertThat(notifIconsVisible!!.visibility).isEqualTo(View.VISIBLE) - assertThat(systemInfoVisible!!.visibility).isEqualTo(View.VISIBLE) + assertThat(systemInfoVisible!!.baseVisibility.visibility).isEqualTo(View.VISIBLE) } @Test @@ -691,14 +722,14 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { testScope.runTest { val clockVisible by collectLastValue(underTest.isClockVisible) val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible) - val systemInfoVisible by collectLastValue(underTest.isSystemInfoVisible) + val systemInfoVisible by collectLastValue(underTest.systemInfoCombinedVis) transitionKeyguardToGone() kosmos.shadeTestUtil.setShadeExpansion(0f) assertThat(clockVisible!!.visibility).isEqualTo(View.VISIBLE) assertThat(notifIconsVisible!!.visibility).isEqualTo(View.VISIBLE) - assertThat(systemInfoVisible!!.visibility).isEqualTo(View.VISIBLE) + assertThat(systemInfoVisible!!.baseVisibility.visibility).isEqualTo(View.VISIBLE) } @Test @@ -707,13 +738,13 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { testScope.runTest { val clockVisible by collectLastValue(underTest.isClockVisible) val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible) - val systemInfoVisible by collectLastValue(underTest.isSystemInfoVisible) + val systemInfoVisible by collectLastValue(underTest.systemInfoCombinedVis) kosmos.sceneContainerRepository.snapToScene(Scenes.Gone) assertThat(clockVisible!!.visibility).isEqualTo(View.VISIBLE) assertThat(notifIconsVisible!!.visibility).isEqualTo(View.VISIBLE) - assertThat(systemInfoVisible!!.visibility).isEqualTo(View.VISIBLE) + assertThat(systemInfoVisible!!.baseVisibility.visibility).isEqualTo(View.VISIBLE) } @Test @@ -722,14 +753,14 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { testScope.runTest { val clockVisible by collectLastValue(underTest.isClockVisible) val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible) - val systemInfoVisible by collectLastValue(underTest.isSystemInfoVisible) + val systemInfoVisible by collectLastValue(underTest.systemInfoCombinedVis) transitionKeyguardToGone() kosmos.shadeTestUtil.setShadeExpansion(1f) assertThat(clockVisible!!.visibility).isEqualTo(View.GONE) assertThat(notifIconsVisible!!.visibility).isEqualTo(View.GONE) - assertThat(systemInfoVisible!!.visibility).isEqualTo(View.GONE) + assertThat(systemInfoVisible!!.baseVisibility.visibility).isEqualTo(View.GONE) } @Test @@ -738,14 +769,14 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { testScope.runTest { val clockVisible by collectLastValue(underTest.isClockVisible) val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible) - val systemInfoVisible by collectLastValue(underTest.isSystemInfoVisible) + val systemInfoVisible by collectLastValue(underTest.systemInfoCombinedVis) transitionKeyguardToGone() kosmos.sceneContainerRepository.snapToScene(Scenes.Shade) assertThat(clockVisible!!.visibility).isEqualTo(View.GONE) assertThat(notifIconsVisible!!.visibility).isEqualTo(View.GONE) - assertThat(systemInfoVisible!!.visibility).isEqualTo(View.GONE) + assertThat(systemInfoVisible!!.baseVisibility.visibility).isEqualTo(View.GONE) } @Test @@ -754,7 +785,7 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { testScope.runTest { val clockVisible by collectLastValue(underTest.isClockVisible) val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible) - val systemInfoVisible by collectLastValue(underTest.isSystemInfoVisible) + val systemInfoVisible by collectLastValue(underTest.systemInfoCombinedVis) // Secure camera is an occluding activity keyguardTransitionRepository.sendTransitionSteps( @@ -766,7 +797,7 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { assertThat(clockVisible!!.visibility).isEqualTo(View.GONE) assertThat(notifIconsVisible!!.visibility).isEqualTo(View.GONE) - assertThat(systemInfoVisible!!.visibility).isEqualTo(View.GONE) + assertThat(systemInfoVisible!!.baseVisibility.visibility).isEqualTo(View.GONE) } @Test @@ -775,7 +806,7 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { testScope.runTest { val clockVisible by collectLastValue(underTest.isClockVisible) val notifIconsVisible by collectLastValue(underTest.isNotificationIconContainerVisible) - val systemInfoVisible by collectLastValue(underTest.isSystemInfoVisible) + val systemInfoVisible by collectLastValue(underTest.systemInfoCombinedVis) kosmos.sceneContainerRepository.snapToScene(Scenes.Lockscreen) // Secure camera is an occluding activity @@ -784,7 +815,7 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() { assertThat(clockVisible!!.visibility).isEqualTo(View.GONE) assertThat(notifIconsVisible!!.visibility).isEqualTo(View.GONE) - assertThat(systemInfoVisible!!.visibility).isEqualTo(View.GONE) + assertThat(systemInfoVisible!!.baseVisibility.visibility).isEqualTo(View.GONE) } private fun activeNotificationsStore(notifications: List<ActiveNotificationModel>) = diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt index b5dbc3fe1b4d..33223aef11ff 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt @@ -29,6 +29,7 @@ import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.DemoMod import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.DemoWifiRepository import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.model.FakeWifiEventModel import com.android.systemui.statusbar.pipeline.wifi.data.repository.prod.WifiRepositoryImpl +import com.android.systemui.user.data.repository.FakeUserRepository import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.kotlinArgumentCaptor @@ -71,6 +72,7 @@ class WifiRepositorySwitcherTest : SysuiTestCase() { private val demoModelFlow = MutableStateFlow<FakeWifiEventModel?>(null) private val mainExecutor = FakeExecutor(FakeSystemClock()) + private val userRepository = FakeUserRepository() private val testDispatcher = UnconfinedTestDispatcher() private val testScope = TestScope(testDispatcher) @@ -82,10 +84,13 @@ class WifiRepositorySwitcherTest : SysuiTestCase() { // Never start in demo mode whenever(demoModeController.isInDemoMode).thenReturn(false) - whenever(wifiPickerTrackerFactory.create(any(), any(), any())).thenReturn(wifiPickerTracker) + whenever(wifiPickerTrackerFactory.create(any(), any(), any(), any())) + .thenReturn(wifiPickerTracker) realImpl = WifiRepositoryImpl( + mContext, + userRepository, testScope.backgroundScope, mainExecutor, testDispatcher, @@ -97,11 +102,7 @@ class WifiRepositorySwitcherTest : SysuiTestCase() { whenever(demoModeWifiDataSource.wifiEvents).thenReturn(demoModelFlow) - demoImpl = - DemoWifiRepository( - demoModeWifiDataSource, - testScope.backgroundScope, - ) + demoImpl = DemoWifiRepository(demoModeWifiDataSource, testScope.backgroundScope) underTest = WifiRepositorySwitcher( diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt index 6b371d74eacc..8a4593032748 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt @@ -19,6 +19,7 @@ package com.android.systemui.user.data.repository import android.app.admin.devicePolicyManager import android.content.pm.UserInfo +import android.internal.statusbar.fakeStatusBarService import android.os.UserHandle import android.os.UserManager import android.provider.Settings @@ -29,6 +30,7 @@ import com.android.systemui.broadcast.broadcastDispatcher import com.android.systemui.kosmos.testDispatcher import com.android.systemui.kosmos.testScope import com.android.systemui.kosmos.useUnconfinedTestDispatcher +import com.android.systemui.res.R import com.android.systemui.settings.FakeUserTracker import com.android.systemui.testKosmos import com.android.systemui.user.data.model.SelectedUserModel @@ -61,6 +63,7 @@ class UserRepositoryImplTest : SysuiTestCase() { private val globalSettings = kosmos.fakeGlobalSettings private val broadcastDispatcher = kosmos.broadcastDispatcher private val devicePolicyManager = kosmos.devicePolicyManager + private val statusBarService = kosmos.fakeStatusBarService @Mock private lateinit var manager: UserManager @@ -72,6 +75,10 @@ class UserRepositoryImplTest : SysuiTestCase() { fun setUp() { MockitoAnnotations.initMocks(this) tracker = FakeUserTracker() + context.orCreateTestableResources.addOverride( + R.bool.config_userSwitchingMustGoThroughLoginScreen, + false, + ) } @Test @@ -323,6 +330,8 @@ class UserRepositoryImplTest : SysuiTestCase() { tracker = tracker, broadcastDispatcher = broadcastDispatcher, devicePolicyManager = devicePolicyManager, + resources = context.resources, + statusBarService = statusBarService, ) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/user/domain/interactor/UserLogoutInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/user/domain/interactor/UserLogoutInteractorTest.kt index 26439df45ba3..f70b42638cda 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/user/domain/interactor/UserLogoutInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/user/domain/interactor/UserLogoutInteractorTest.kt @@ -49,35 +49,78 @@ class UserLogoutInteractorTest : SysuiTestCase() { @Before fun setUp() { userRepository.setUserInfos(USER_INFOS) - runBlocking { userRepository.setSelectedUserInfo(USER_INFOS[1]) } + runBlocking { userRepository.setSelectedUserInfo(USER_INFOS[2]) } + userRepository.setLogoutToSystemUserEnabled(false) + userRepository.setSecondaryUserLogoutEnabled(false) } @Test - fun logOut_doesNothing_whenAdminDisabledSecondaryLogout() { + fun logOut_doesNothing_whenBothLogoutOptionsAreDisabled() { testScope.runTest { val isLogoutEnabled by collectLastValue(underTest.isLogoutEnabled) - val lastLogoutCount = userRepository.logOutSecondaryUserCallCount - userRepository.setSecondaryUserLogoutEnabled(false) + val secondaryUserLogoutCount = userRepository.logOutSecondaryUserCallCount + val logoutToSystemUserCount = userRepository.logOutToSystemUserCallCount assertThat(isLogoutEnabled).isFalse() underTest.logOut() + assertThat(userRepository.logOutSecondaryUserCallCount) + .isEqualTo(secondaryUserLogoutCount) + assertThat(userRepository.logOutToSystemUserCallCount) + .isEqualTo(logoutToSystemUserCount) + } + } + + @Test + fun logOut_logsOutSecondaryUser_whenAdminEnabledSecondaryLogout() { + testScope.runTest { + val isLogoutEnabled by collectLastValue(underTest.isLogoutEnabled) + val lastLogoutCount = userRepository.logOutSecondaryUserCallCount + val logoutToSystemUserCount = userRepository.logOutToSystemUserCallCount + userRepository.setSecondaryUserLogoutEnabled(true) + assertThat(isLogoutEnabled).isTrue() + underTest.logOut() + assertThat(userRepository.logOutSecondaryUserCallCount).isEqualTo(lastLogoutCount + 1) + assertThat(userRepository.logOutToSystemUserCallCount) + .isEqualTo(logoutToSystemUserCount) + } + } + + @Test + fun logOut_logsOutToSystemUser_whenLogoutToSystemUserIsEnabled() { + testScope.runTest { + val isLogoutEnabled by collectLastValue(underTest.isLogoutEnabled) + val lastLogoutCount = userRepository.logOutSecondaryUserCallCount + val logoutToSystemUserCount = userRepository.logOutToSystemUserCallCount + userRepository.setLogoutToSystemUserEnabled(true) + assertThat(isLogoutEnabled).isTrue() + underTest.logOut() assertThat(userRepository.logOutSecondaryUserCallCount).isEqualTo(lastLogoutCount) + assertThat(userRepository.logOutToSystemUserCallCount) + .isEqualTo(logoutToSystemUserCount + 1) } } @Test - fun logOut_logsOut_whenAdminEnabledSecondaryLogout() { + fun logOut_secondaryUserTakesPrecedence() { testScope.runTest { val isLogoutEnabled by collectLastValue(underTest.isLogoutEnabled) val lastLogoutCount = userRepository.logOutSecondaryUserCallCount + val logoutToSystemUserCount = userRepository.logOutToSystemUserCallCount + userRepository.setLogoutToSystemUserEnabled(true) userRepository.setSecondaryUserLogoutEnabled(true) assertThat(isLogoutEnabled).isTrue() underTest.logOut() assertThat(userRepository.logOutSecondaryUserCallCount).isEqualTo(lastLogoutCount + 1) + assertThat(userRepository.logOutToSystemUserCallCount) + .isEqualTo(logoutToSystemUserCount) } } companion object { private val USER_INFOS = - listOf(UserInfo(0, "System user", 0), UserInfo(10, "Regular user", 0)) + listOf( + UserInfo(0, "System user", 0), + UserInfo(10, "Regular user", 0), + UserInfo(11, "Secondary user", 0), + ) } } diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt index 7d55169e048a..89da46544f1e 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt @@ -13,11 +13,20 @@ */ package com.android.systemui.plugins.clocks +import android.content.Context import android.graphics.Rect import android.graphics.drawable.Drawable +import android.util.DisplayMetrics import android.view.View import androidx.constraintlayout.widget.ConstraintSet +import androidx.constraintlayout.widget.ConstraintSet.BOTTOM +import androidx.constraintlayout.widget.ConstraintSet.END +import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID +import androidx.constraintlayout.widget.ConstraintSet.START +import androidx.constraintlayout.widget.ConstraintSet.TOP +import androidx.constraintlayout.widget.ConstraintSet.WRAP_CONTENT import com.android.internal.annotations.Keep +import com.android.internal.policy.SystemBarUtils import com.android.systemui.log.core.MessageBuffer import com.android.systemui.plugins.Plugin import com.android.systemui.plugins.annotations.GeneratedImport @@ -149,7 +158,7 @@ interface ClockFaceLayout { @ProtectedReturn("return constraints;") /** Custom constraints to apply to preview ConstraintLayout. */ - fun applyPreviewConstraints(constraints: ConstraintSet): ConstraintSet + fun applyPreviewConstraints(context: Context, constraints: ConstraintSet): ConstraintSet fun applyAodBurnIn(aodBurnInModel: AodClockBurnInModel) } @@ -169,13 +178,84 @@ class DefaultClockFaceLayout(val view: View) : ClockFaceLayout { return constraints } - override fun applyPreviewConstraints(constraints: ConstraintSet): ConstraintSet { - return constraints + override fun applyPreviewConstraints( + context: Context, + constraints: ConstraintSet, + ): ConstraintSet { + return applyDefaultPreviewConstraints(context, constraints) } override fun applyAodBurnIn(aodBurnInModel: AodClockBurnInModel) { // Default clock doesn't need detailed control of view } + + companion object { + fun applyDefaultPreviewConstraints( + context: Context, + constraints: ConstraintSet, + ): ConstraintSet { + constraints.apply { + val lockscreenClockViewLargeId = getId(context, "lockscreen_clock_view_large") + constrainWidth(lockscreenClockViewLargeId, WRAP_CONTENT) + constrainHeight(lockscreenClockViewLargeId, WRAP_CONTENT) + constrainMaxHeight(lockscreenClockViewLargeId, 0) + + val largeClockTopMargin = + SystemBarUtils.getStatusBarHeight(context) + + getDimen(context, "small_clock_padding_top") + + getDimen(context, "keyguard_smartspace_top_offset") + + getDimen(context, "date_weather_view_height") + + getDimen(context, "enhanced_smartspace_height") + connect(lockscreenClockViewLargeId, TOP, PARENT_ID, TOP, largeClockTopMargin) + connect(lockscreenClockViewLargeId, START, PARENT_ID, START) + connect(lockscreenClockViewLargeId, END, PARENT_ID, END) + + // In preview, we'll show UDFPS icon for UDFPS devices + // and nothing for non-UDFPS devices, + // and we're not planning to add this vide in clockHostView + // so we only need position of device entry icon to constrain clock + // Copied calculation codes from applyConstraints in DefaultDeviceEntrySection + val bottomPaddingPx = getDimen(context, "lock_icon_margin_bottom") + val defaultDensity = + DisplayMetrics.DENSITY_DEVICE_STABLE.toFloat() / + DisplayMetrics.DENSITY_DEFAULT.toFloat() + val lockIconRadiusPx = (defaultDensity * 36).toInt() + val clockBottomMargin = bottomPaddingPx + 2 * lockIconRadiusPx + + connect(lockscreenClockViewLargeId, BOTTOM, PARENT_ID, BOTTOM, clockBottomMargin) + val smallClockViewId = getId(context, "lockscreen_clock_view") + constrainWidth(smallClockViewId, WRAP_CONTENT) + constrainHeight(smallClockViewId, getDimen(context, "small_clock_height")) + connect( + smallClockViewId, + START, + PARENT_ID, + START, + getDimen(context, "clock_padding_start") + + getDimen(context, "status_view_margin_horizontal"), + ) + val smallClockTopMargin = + getDimen(context, "keyguard_clock_top_margin") + + SystemBarUtils.getStatusBarHeight(context) + connect(smallClockViewId, TOP, PARENT_ID, TOP, smallClockTopMargin) + } + return constraints + } + + fun getId(context: Context, name: String): Int { + val packageName = context.packageName + val res = context.packageManager.getResourcesForApplication(packageName) + val id = res.getIdentifier(name, "id", packageName) + return id + } + + fun getDimen(context: Context, name: String): Int { + val packageName = context.packageName + val res = context.packageManager.getResourcesForApplication(packageName) + val id = res.getIdentifier(name, "dimen", packageName) + return if (id == 0) 0 else res.getDimensionPixelSize(id) + } + } } /** Events that should call when various rendering parameters change */ diff --git a/packages/SystemUI/res/layout/split_clock_view.xml b/packages/SystemUI/res/layout/split_clock_view.xml deleted file mode 100644 index 8198f0333a94..000000000000 --- a/packages/SystemUI/res/layout/split_clock_view.xml +++ /dev/null @@ -1,54 +0,0 @@ -<?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 - --> - -<!-- Extends LinearLayout --> -<com.android.systemui.statusbar.policy.SplitClockView - xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - > - <TextClock - android:id="@+id/time_view" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:singleLine="true" - android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Clock" - android:textSize="@dimen/qs_time_collapsed_size" - android:textColor="?android:attr/textColorPrimary" - /> - <TextClock - android:id="@+id/am_pm_view" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:singleLine="true" - android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Clock" - android:textSize="@dimen/qs_time_collapsed_size" - android:textColor="?android:attr/textColorPrimary" - android:importantForAccessibility="no" - /> - - <!-- Empty text view so we have the same height when expanded/collapsed--> - <TextView - android:id="@+id/empty_time_view" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:visibility="gone" - android:singleLine="true" - android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Clock" - android:textColor="?android:attr/textColorPrimary" - /> -</com.android.systemui.statusbar.policy.SplitClockView> diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml index e90f055f2538..f187ce62ddb2 100644 --- a/packages/SystemUI/res/layout/volume_dialog.xml +++ b/packages/SystemUI/res/layout/volume_dialog.xml @@ -13,50 +13,58 @@ See the License for the specific language governing permissions and limitations under the License. --> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" - android:id="@+id/volume_dialog_container" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="right" - android:divider="@drawable/volume_dialog_floating_sliders_spacer" - android:orientation="horizontal" - android:showDividers="middle|end|beginning" - android:theme="@style/volume_dialog_theme"> + android:id="@+id/volume_dialog_root" + android:layout_width="match_parent" + android:layout_height="match_parent"> <LinearLayout - android:id="@+id/volume_dialog_floating_sliders_container" + android:id="@+id/volume_dialog_container" android:layout_width="wrap_content" - android:layout_height="match_parent" + android:layout_height="wrap_content" + android:layout_gravity="center_vertical|end" android:divider="@drawable/volume_dialog_floating_sliders_spacer" - android:gravity="bottom" android:orientation="horizontal" - android:paddingBottom="@dimen/volume_dialog_floating_sliders_bottom_padding" - android:showDividers="middle" /> + android:showDividers="middle|end|beginning"> - <LinearLayout - android:id="@+id/volume_dialog" - android:layout_width="@dimen/volume_dialog_width" - android:layout_height="wrap_content" - android:background="@drawable/volume_dialog_background" - android:divider="@drawable/volume_dialog_spacer" - android:paddingVertical="@dimen/volume_dialog_vertical_padding" - android:gravity="center_horizontal" - android:orientation="vertical" - android:showDividers="middle"> - - <include layout="@layout/volume_ringer_drawer" /> - - <include layout="@layout/volume_dialog_slider" /> - - <ImageButton - android:id="@+id/volume_dialog_settings" - android:layout_width="@dimen/volume_dialog_button_size" - android:layout_height="@dimen/volume_dialog_button_size" - android:background="@drawable/ripple_drawable_20dp" - android:contentDescription="@string/accessibility_volume_settings" - android:soundEffectsEnabled="false" - android:src="@drawable/horizontal_ellipsis" - android:tint="?androidprv:attr/materialColorPrimary" /> + <LinearLayout + android:id="@+id/volume_dialog_floating_sliders_container" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:divider="@drawable/volume_dialog_floating_sliders_spacer" + android:gravity="bottom" + android:orientation="horizontal" + android:paddingBottom="@dimen/volume_dialog_floating_sliders_bottom_padding" + android:showDividers="middle" /> + + <LinearLayout + android:id="@+id/volume_dialog" + android:layout_width="@dimen/volume_dialog_width" + android:layout_height="wrap_content" + android:background="@drawable/volume_dialog_background" + android:clipChildren="false" + android:clipToOutline="false" + android:clipToPadding="false" + android:divider="@drawable/volume_dialog_spacer" + android:gravity="center_horizontal" + android:orientation="vertical" + android:paddingVertical="@dimen/volume_dialog_vertical_padding" + android:showDividers="middle"> + + <include layout="@layout/volume_ringer_drawer" /> + + <include layout="@layout/volume_dialog_slider" /> + + <ImageButton + android:id="@+id/volume_dialog_settings" + android:layout_width="@dimen/volume_dialog_button_size" + android:layout_height="@dimen/volume_dialog_button_size" + android:background="@drawable/ripple_drawable_20dp" + android:contentDescription="@string/accessibility_volume_settings" + android:soundEffectsEnabled="false" + android:src="@drawable/horizontal_ellipsis" + android:tint="?androidprv:attr/materialColorPrimary" /> + </LinearLayout> </LinearLayout> -</LinearLayout>
\ No newline at end of file +</FrameLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml index 2f2981b33fec..048ddaf735da 100644 --- a/packages/SystemUI/res/values-af/strings.xml +++ b/packages/SystemUI/res/values-af/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Invoer"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Gehoortoestelle"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Skakel tans aan …"</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Outodraai"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Outodraai skerm"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Ligging"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"Kon nie voorafstelling opdateer nie"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Voorafstelling"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Intydse Onderskrifte"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Deblokkeer toestelmikrofoon?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Deblokkeer toestelkamera?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Deblokkeer toestelkamera en mikrofoon?"</string> @@ -698,8 +702,7 @@ <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Vasgestel"</string> <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Kopnasporing"</string> <string name="volume_ringer_change" msgid="3574969197796055532">"Tik om luiermodus te verander"</string> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"luiermodus"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"demp"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ontdemp"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibreer"</string> @@ -869,6 +872,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Skakel oor na app regs of onder terwyl jy verdeelde skerm gebruik"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Skakel oor na app links of bo terwyl jy verdeelde skerm gebruik"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"Tydens verdeelde skerm: verplaas ’n app van een skerm na ’n ander"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Invoer"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"Skakel oor na volgende taal"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"Skakel oor na vorige taal"</string> @@ -1411,15 +1416,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Pasmaak"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Klaar"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Vou ikoon uit"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"of"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Sleephandvatsel"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Sleutelbordinstellings"</string> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <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-af/tiles_states_strings.xml b/packages/SystemUI/res/values-af/tiles_states_strings.xml index 1b4781d24ebc..4afae3328cf7 100644 --- a/packages/SystemUI/res/values-af/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-af/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"Af"</item> <item msgid="3028994095749238254">"Aan"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml index 129c908ab539..9d03a911b9d3 100644 --- a/packages/SystemUI/res/values-am/strings.xml +++ b/packages/SystemUI/res/values-am/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"ግቤት"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"መስሚያ አጋዥ መሣሪያዎች"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"በማብራት ላይ..."</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"በራስ ሰር አሽከርክር"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"ማያ ገጽን በራስ-አሽከርክር"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"አካባቢ"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"ቅድመ-ቅምጥን ማዘመን አልተቻለም"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"ቅድመ-ቅምጥ"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"የቀጥታ መግለጫ ጽሑፍ"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"የመሣሪያ ማይክሮፎን እገዳ ይነሳ?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"የመሣሪያ ካሜራ እገዳ ይነሳ?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"የመሣሪያ ካሜራ እና ማይክሮፎን እገዳ ይነሳ?"</string> @@ -698,8 +702,7 @@ <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> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"ደዋይ ሁነታ"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"ድምጸ-ከል አድርግ"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ድምጸ-ከልን አንሳ"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"ንዘር"</string> @@ -869,6 +872,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"የተከፈለ ማያ ገጽን ሲጠቀሙ በቀኝ ወይም ከታች ወዳለ መተግበሪያ ይቀይሩ"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"የተከፈለ ማያ ገጽን ሲጠቀሙ በቀኝ ወይም ከላይ ወዳለ መተግበሪያ ይቀይሩ"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"በተከፈለ ማያ ገጽ ወቅት፡- መተግበሪያን ከአንዱ ወደ ሌላው ተካ"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"ግቤት"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"ወደ ቀጣዩ ቋንቋ ቀይር"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"ወደ ቀዳሚ ቋንቋ ቀይር"</string> @@ -1411,15 +1416,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"አብጅ"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"ተከናውኗል"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"መዘርጊያ አዶ"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ወይም"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"መያዣ ይጎትቱ"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"የቁልፍ ሰሌዳ ቅንብሮች"</string> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <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-am/tiles_states_strings.xml b/packages/SystemUI/res/values-am/tiles_states_strings.xml index a3c590c33a6f..8601132cf141 100644 --- a/packages/SystemUI/res/values-am/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-am/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"አጥፋ"</item> <item msgid="3028994095749238254">"አብራ"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml index 48fc91377a17..062ab78d1b31 100644 --- a/packages/SystemUI/res/values-ar/strings.xml +++ b/packages/SystemUI/res/values-ar/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"الإدخال"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"سماعات الأذن الطبية"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"جارٍ التفعيل…"</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"التدوير التلقائي"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"التدوير التلقائي للشاشة"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"الموقع الجغرافي"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"تعذَّر تعديل الإعداد المسبق"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"الإعدادات المسبقة"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"النسخ النصي التلقائي"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"هل تريد إزالة حظر ميكروفون الجهاز؟"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"هل تريد إزالة حظر كاميرا الجهاز؟"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"هل تريد إزالة حظر الكاميرا والميكروفون؟"</string> @@ -589,8 +593,7 @@ <string name="media_projection_action_text" msgid="3634906766918186440">"البدء الآن"</string> <string name="empty_shade_text" msgid="8935967157319717412">"ما مِن إشعارات"</string> <string name="no_unseen_notif_text" msgid="395512586119868682">"ما مِن إشعارات جديدة"</string> - <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) --> - <skip /> + <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"ميزة \"تخفيض الإشعارات الصوتية والاهتزاز\" مفعَّلة الآن"</string> <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"يتم تلقائيًا خفض مستوى صوت جهازك والتنبيهات لمدة تصل إلى دقيقتين عند تلقّي إشعارات كثيرة في آنٍ واحد."</string> <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"إيقاف"</string> <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"افتَح قفل الشاشة لعرض الإشعارات الأقدم."</string> @@ -698,8 +701,7 @@ <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> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"وضع الرنين"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"كتم الصوت"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"إعادة الصوت"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"اهتزاز"</string> @@ -869,6 +871,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"التبديل إلى التطبيق على اليسار أو الأسفل أثناء استخدام \"تقسيم الشاشة\""</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"التبديل إلى التطبيق على اليمين أو الأعلى أثناء استخدام \"تقسيم الشاشة\""</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"استبدال تطبيق بآخر في وضع \"تقسيم الشاشة\""</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"الإدخال"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"التبديل إلى اللغة التالية"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"التبديل إلى اللغة السابقة"</string> @@ -1411,15 +1415,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"تخصيص"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"تم"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"رمز التوسيع"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"أو"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"مقبض السحب"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"إعدادات لوحة المفاتيح"</string> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <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/tiles_states_strings.xml b/packages/SystemUI/res/values-ar/tiles_states_strings.xml index a89650aa6e19..f985e2f001c6 100644 --- a/packages/SystemUI/res/values-ar/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-ar/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"غير مفعَّلة"</item> <item msgid="3028994095749238254">"مفعَّلة"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml index 96446c545561..7b3f0bbbc93c 100644 --- a/packages/SystemUI/res/values-as/strings.xml +++ b/packages/SystemUI/res/values-as/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"ইনপুট"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"শ্ৰৱণ যন্ত্ৰ"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"অন কৰি থকা হৈছে…"</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"স্বয়ং-ঘূৰ্ণন"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"স্বয়ং-ঘূৰ্ণন স্ক্ৰীন"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"অৱস্থান"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"প্ৰিছেট আপডে’ট কৰিব পৰা নগ’ল"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"প্ৰিছেট"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"লাইভ কেপশ্বন"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ডিভাইচৰ মাইক্ৰ\'ফ\'ন অৱৰোধৰ পৰা আঁতৰাবনে?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"ডিভাইচৰ কেমেৰা অৱৰোধৰ পৰা আঁতৰাবনে?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"ডিভাইচৰ কেমেৰা আৰু মাইক্ৰ\'ফ\'ন অৱৰোধৰ পৰা আঁতৰাবনে?"</string> @@ -698,8 +702,7 @@ <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> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"ৰিংগাৰ ম’ড"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"মিউট কৰক"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"আনমিউট কৰক"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"কম্পন কৰক"</string> @@ -869,6 +872,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"বিভাজিত স্ক্ৰীন ব্যৱহাৰ কৰাৰ সময়ত সোঁফালে অথবা তলত থকা এপলৈ সলনি কৰক"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"বিভাজিত স্ক্ৰীন ব্যৱহাৰ কৰাৰ সময়ত বাওঁফালে অথবা ওপৰত থকা এপলৈ সলনি কৰক"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"বিভাজিত স্ক্ৰীনৰ ব্যৱহাৰ কৰাৰ সময়ত: কোনো এপ্ এখন স্ক্ৰীনৰ পৰা আনখনলৈ নিয়ক"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"ইনপুট"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"পৰৱৰ্তী ভাষাটোলৈ সলনি কৰক"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"পূৰ্বৰ ভাষালৈ সলনি কৰক"</string> @@ -1411,15 +1416,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"কাষ্টমাইজ কৰক"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"হ’ল"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"বিস্তাৰ কৰাৰ চিহ্ন"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"অথবা"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ড্ৰেগ হেণ্ডেল"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"কীব’ৰ্ডৰ ছেটিং"</string> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <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> @@ -1435,7 +1454,7 @@ <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"সুন্দৰ!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"আপুনি উভতি যোৱাৰ নিৰ্দেশটো সম্পূৰ্ণ কৰিলে।"</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"গৃহ পৃষ্ঠালৈ যাওক"</string> - <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"আপোনাৰ টাচ্চপেডৰ তিনিটা আঙুলিৰে ওপৰলৈ ছোৱাইপ কৰক"</string> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"আপোনাৰ টাচ্চপেডত তিনিটা আঙুলিৰে ওপৰলৈ ছোৱাইপ কৰক"</string> <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"বঢ়িয়া!"</string> <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"আপুনি গৃহ স্ক্ৰীনলৈ যোৱাৰ নিৰ্দেশটো সম্পূৰ্ণ কৰিলে"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"শেহতীয়া এপ্সমূহ চাওক"</string> diff --git a/packages/SystemUI/res/values-as/tiles_states_strings.xml b/packages/SystemUI/res/values-as/tiles_states_strings.xml index e978fe279a6e..3ec2f5c67557 100644 --- a/packages/SystemUI/res/values-as/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-as/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"অফ আছে"</item> <item msgid="3028994095749238254">"অন আছে"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml index 2932b1950909..1c84a590d6b9 100644 --- a/packages/SystemUI/res/values-az/strings.xml +++ b/packages/SystemUI/res/values-az/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Giriş"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Eşitmə aparatları"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Aktiv edilir..."</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Avtodönüş"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Ekranın avtomatik dönməsi"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Məkan"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"Hazır ayar güncəllənmədi"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Hazır Ayar"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Canlı Altyazı"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Cihaz mikrofonu blokdan çıxarılsın?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Cihaz kamerası blokdan çıxarılsın?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Cihaz kamerası və mikrofonu blokdan çıxarılsın?"</string> @@ -698,8 +702,7 @@ <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Sabit"</string> <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Baş izləməsi"</string> <string name="volume_ringer_change" msgid="3574969197796055532">"Zəng rejimini dəyişmək üçün toxunun"</string> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"zəng səsi rejimi"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"susdurun"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"səssiz rejimdən çıxarın"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrasiya"</string> @@ -869,6 +872,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Bölünmüş ekran istifadə edərkən sağda və ya aşağıda tətbiqə keçin"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Bölünmüş ekran istifadə edərkən solda və ya yuxarıda tətbiqə keçin"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"Bölünmüş ekran rejimində: tətbiqi birindən digərinə dəyişin"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Daxiletmə"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"Növbəti dilə keçin"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"Əvvəlki dilə keçin"</string> @@ -1411,15 +1416,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Fərdiləşdirin"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Hazırdır"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"İkonanı genişləndirin"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"və ya"</string> <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> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <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-az/tiles_states_strings.xml b/packages/SystemUI/res/values-az/tiles_states_strings.xml index c24f4029e415..4eea105107c1 100644 --- a/packages/SystemUI/res/values-az/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-az/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"Deaktiv"</item> <item msgid="3028994095749238254">"Aktiv"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml index 7b571b93f1ad..f260f5369f7c 100644 --- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml +++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Unos"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Slušni aparati"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Uključuje se..."</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automatska rotacija"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Automatsko rotiranje ekrana"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Lokacija"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"Ažuriranje zadatih podešavanja nije uspelo"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Unapred određena podešavanja"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Titl uživo"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Želite da odblokirate mikrofon uređaja?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Želite da odblokirate kameru uređaja?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Želite da odblokirate kameru i mikrofon uređaja?"</string> @@ -589,8 +593,7 @@ <string name="media_projection_action_text" msgid="3634906766918186440">"Započni"</string> <string name="empty_shade_text" msgid="8935967157319717412">"Nema obaveštenja"</string> <string name="no_unseen_notif_text" msgid="395512586119868682">"Nema novih obaveštenja"</string> - <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) --> - <skip /> + <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"Utišavanje obaveštenja je sada uključeno"</string> <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Zvuk i broj upozorenja na uređaju se automatski smanjuju na 2 minuta kada dobijete previše obaveštenja."</string> <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Isključi"</string> <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Otključajte za starija obaveštenja"</string> @@ -698,8 +701,7 @@ <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Fiksno"</string> <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Praćenje glave"</string> <string name="volume_ringer_change" msgid="3574969197796055532">"Dodirnite da biste promenili režim zvona"</string> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"režim zvona"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"isključite zvuk"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"uključite zvuk"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibracija"</string> @@ -869,6 +871,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Pređi u aplikaciju zdesna ili ispod dok je podeljen ekran"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Pređite u aplikaciju sleva ili iznad dok koristite podeljeni ekran"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"U režimu podeljenog ekrana: zamena jedne aplikacije drugom"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Unos"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"Pređi na sledeći jezik"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"Pređi na prethodni jezik"</string> @@ -1411,15 +1415,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Prilagodi"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Gotovo"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona za proširivanje"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ili"</string> <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> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <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 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-b+sr+Latn/tiles_states_strings.xml b/packages/SystemUI/res/values-b+sr+Latn/tiles_states_strings.xml index df0b78664cba..3f8841afb4f2 100644 --- a/packages/SystemUI/res/values-b+sr+Latn/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-b+sr+Latn/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"Isključeno"</item> <item msgid="3028994095749238254">"Uključeno"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml index 2d2a010579bc..a303c11ef0fc 100644 --- a/packages/SystemUI/res/values-be/strings.xml +++ b/packages/SystemUI/res/values-be/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Увод"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Слыхавыя апараты"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Уключэнне…"</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Аўтапаварот"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Аўтаматычны паварот экрана"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Месцазнаходжанне"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"Не ўдалося абнавіць набор налад"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Набор налад"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Аўтаматычныя субцітры"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Разблакіраваць мікрафон прылады?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Разблакіраваць камеру прылады?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Разблакіраваць камеру і мікрафон прылады?"</string> @@ -589,8 +593,7 @@ <string name="media_projection_action_text" msgid="3634906766918186440">"Пачаць зараз"</string> <string name="empty_shade_text" msgid="8935967157319717412">"Апавяшчэнняў няма"</string> <string name="no_unseen_notif_text" msgid="395512586119868682">"Няма новых апавяшчэнняў"</string> - <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) --> - <skip /> + <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"Зніжэнне гучнасці апавяшчэнняў зараз уключана"</string> <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Калі адначасова прыходзіць шмат апавяшчэнняў, гук прылады і абвестак зніжаецца на час да 2 хвілін."</string> <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Выключыць"</string> <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Разблакіруйце, каб убачыць усе апавяшчэнні"</string> @@ -698,8 +701,7 @@ <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> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"рэжым званка"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"выключыць гук"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"уключыць гук"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"вібрыраваць"</string> @@ -869,6 +871,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Пераключыцца на праграму справа або ўнізе на падзеленым экране"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Пераключыцца на праграму злева або ўверсе на падзеленым экране"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"У рэжыме падзеленага экрана замяніць адну праграму на іншую"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Увод"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"Пераключыцца на наступную мову"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"Пераключыцца на папярэднюю мову"</string> @@ -1411,15 +1415,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Наладзіць"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Гатова"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Значок \"Разгарнуць\""</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"або"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Маркер перацягвання"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Налады клавіятуры"</string> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <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-be/tiles_states_strings.xml b/packages/SystemUI/res/values-be/tiles_states_strings.xml index 33e704cae0b1..85602864dc76 100644 --- a/packages/SystemUI/res/values-be/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-be/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"Выключана"</item> <item msgid="3028994095749238254">"Уключана"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml index c2d5b9e0e1b9..a127515e8f19 100644 --- a/packages/SystemUI/res/values-bg/strings.xml +++ b/packages/SystemUI/res/values-bg/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Вход"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Слухови апарати"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Включва се..."</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Авт. ориентация"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Автоматично завъртане на екрана"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Местоположение"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"Предварително зададените настройки не бяха актуализирани"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Предварително зададено"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Надписи на живо"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Да се отблокира ли микрофонът на устройството?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Да се отблокира ли камерата на устройството?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Да се отблокират ли камерата и микрофонът на устройството?"</string> @@ -589,8 +593,7 @@ <string name="media_projection_action_text" msgid="3634906766918186440">"Стартиране сега"</string> <string name="empty_shade_text" msgid="8935967157319717412">"Няма известия"</string> <string name="no_unseen_notif_text" msgid="395512586119868682">"Няма нови известия"</string> - <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) --> - <skip /> + <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"Изчакването за известията вече е включено"</string> <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Силата на звука и сигналите на у-вото се намаляват за до 2 минути, когато получавате твърде много известия наведнъж."</string> <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Изключване"</string> <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Отключете за достъп до по-стари известия"</string> @@ -698,8 +701,7 @@ <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> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"режим на звънене"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"спиране"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"пускане"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"вибриране"</string> @@ -869,6 +871,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Превключване към приложението вдясно/отдолу в режима на разделен екран"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Превключване към приложението вляво/отгоре в режима на разделен екран"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"При разделен екран: замяна на дадено приложение с друго"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Въвеждане"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"Превключване към следващия език"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"Превключване към предишния език"</string> @@ -1411,15 +1415,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Персонализиране"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Готово"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Икона за разгъване"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"или"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Манипулатор за преместване с плъзгане"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Настройки на клавиатурата"</string> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <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> @@ -1435,7 +1453,7 @@ <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Чудесно!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Изпълнихте жеста за връщане назад."</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Към началния екран"</string> - <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Прекарайте три пръста нагоре по сензорния панел"</string> + <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Плъзнете три пръста нагоре по сензорния панел"</string> <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Отлично!"</string> <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Изпълнихте жеста за преминаване към началния екран"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Преглед на скорошните приложения"</string> diff --git a/packages/SystemUI/res/values-bg/tiles_states_strings.xml b/packages/SystemUI/res/values-bg/tiles_states_strings.xml index e2fd65360020..9b808de65480 100644 --- a/packages/SystemUI/res/values-bg/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-bg/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"Изкл."</item> <item msgid="3028994095749238254">"Вкл."</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml index 5f8ea8bb610a..ae810a612d28 100644 --- a/packages/SystemUI/res/values-bn/strings.xml +++ b/packages/SystemUI/res/values-bn/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"ইনপুট"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"হিয়ারিং এড"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"চালু করা হচ্ছে…"</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"নিজে থেকে ঘুরবে"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"অটো-রোটেট স্ক্রিন"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"লোকেশন"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"প্রিসেট আপডেট করা যায়নি"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"প্রিসেট"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"লাইভ ক্যাপশন"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ডিভাইসের মাইক্রোফোন আনব্লক করতে চান?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"ডিভাইসের ক্যামেরা আনব্লক করতে চান?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"ডিভাইসের ক্যামেরা এবং মাইক্রোফোন আনব্লক করতে চান?"</string> @@ -698,8 +702,7 @@ <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> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"রিঙ্গার মোড"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"মিউট করুন"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"আনমিউট করুন"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"ভাইব্রেট করান"</string> @@ -869,6 +872,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"স্প্লিট স্ক্রিন ব্যবহার করার সময় ডানদিকের বা নিচের অ্যাপে পাল্টে নিন"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"স্প্লিট স্ক্রিন ব্যবহার করার সময় বাঁদিকের বা উপরের অ্যাপে পাল্টে নিন"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"\'স্প্লিট স্ক্রিন\' থাকাকালীন: একটি অ্যাপ থেকে অন্যটিতে পাল্টান"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"ইনপুট"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"পরবর্তী ভাষায় পাল্টান"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"আগের ভাষায় পাল্টান"</string> @@ -1411,15 +1416,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"কাস্টমাইজ করুন"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"হয়ে গেছে"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"আইকন বড় করুন"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"অথবা"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"টেনে আনার হ্যান্ডেল"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"কীবোর্ড সেটিংস"</string> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <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-bn/tiles_states_strings.xml b/packages/SystemUI/res/values-bn/tiles_states_strings.xml index 6e4dfbfbf745..dd5b40636fb6 100644 --- a/packages/SystemUI/res/values-bn/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-bn/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"বন্ধ আছে"</item> <item msgid="3028994095749238254">"চালু আছে"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml index 32f6be34cd52..df562ef3508a 100644 --- a/packages/SystemUI/res/values-bs/strings.xml +++ b/packages/SystemUI/res/values-bs/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Ulaz"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Slušni aparati"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Uključivanje…"</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automatsko rotiranje"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Automatsko rotiranje ekrana"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Lokacija"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"Ažuriranje zadane postavke nije uspjelo"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Zadana postavka"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Automatski titlovi"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Deblokirati mikrofon uređaja?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Deblokirati kameru uređaja?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Deblokirati kameru i mikrofon uređaja?"</string> @@ -589,8 +593,7 @@ <string name="media_projection_action_text" msgid="3634906766918186440">"Započni odmah"</string> <string name="empty_shade_text" msgid="8935967157319717412">"Nema obavještenja"</string> <string name="no_unseen_notif_text" msgid="395512586119868682">"Nema novih obavještenja"</string> - <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) --> - <skip /> + <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"Stišavanje obavijesti sada je uključeno"</string> <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Jačina zvuka uređaja i obavještenja se automatski stišavaju do 2 minute kada odjednom dobijete previše obavještenja."</string> <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Isključi"</string> <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Otključajte da vidite starija obavještenja"</string> @@ -698,8 +701,7 @@ <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Fiksno"</string> <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Praćenje položaja glave"</string> <string name="volume_ringer_change" msgid="3574969197796055532">"Dodirnite da promijenite način rada zvuka zvona"</string> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"način rada za zvuk zvona"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"isključite zvuk"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"uključite zvuk"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibriranje"</string> @@ -869,6 +871,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Prelazak u aplikaciju desno ili ispod uz podijeljeni ekran"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Pređite u aplikaciju lijevo ili iznad dok koristite podijeljeni ekran"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"Za vrijeme podijeljenog ekrana: zamjena jedne aplikacije drugom"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Unos"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"Prebacivanje na sljedeći jezik"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"Prebacivanje na prethodni jezik"</string> @@ -1411,15 +1415,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Prilagođavanje"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Gotovo"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona proširivanja"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ili"</string> <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> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <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> @@ -1437,7 +1455,7 @@ <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Odlazak na početni ekran"</string> <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Prevucite nagore s tri prsta na dodirnoj podlozi"</string> <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Sjajno!"</string> - <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Savladali ste pokret za otvaranje početnog ekrana"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Savladali ste pokret za odlazak na početni ekran"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Prikaz nedavnih aplikacija"</string> <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Prevucite nagore i zadržite s tri prsta na dodirnoj podlozi"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Sjajno!"</string> diff --git a/packages/SystemUI/res/values-bs/tiles_states_strings.xml b/packages/SystemUI/res/values-bs/tiles_states_strings.xml index df0b78664cba..3f8841afb4f2 100644 --- a/packages/SystemUI/res/values-bs/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-bs/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"Isključeno"</item> <item msgid="3028994095749238254">"Uključeno"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml index 8a700ddd2921..0ecd8c0e0c23 100644 --- a/packages/SystemUI/res/values-ca/strings.xml +++ b/packages/SystemUI/res/values-ca/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Entrada"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Audiòfons"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"S\'està activant…"</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Gira automàticament"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Gira la pantalla automàticament"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Ubicació"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"No s\'ha pogut actualitzar el valor predefinit"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Valors predefinits"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Subtítols instantanis"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Vols desbloquejar el micròfon del dispositiu?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Vols desbloquejar la càmera del dispositiu?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Vols desbloquejar la càmera i el micròfon del dispositiu?"</string> @@ -559,8 +563,8 @@ <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Vols emetre la pantalla?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Emet una aplicació"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_entire_screen" msgid="8389508187954155307">"Emet tota la pantalla"</string> - <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Quan emets tota la pantalla, qualsevol cosa que es mostra en pantalla és visible. Per aquest motiu, ves amb compte amb elements com les contrasenyes, les dades de pagament, els missatges, les fotos, i l\'àudio i el vídeo."</string> - <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Quan emets una aplicació, qualsevol cosa que es mostra o que es reprodueix en aquesta aplicació és visible. Per aquest motiu, ves amb compte amb elements com les contrasenyes, les dades de pagament, els missatges, les fotos, i l\'àudio i el vídeo."</string> + <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="4040447861037324017">"Quan emets tota la pantalla, qualsevol cosa que s\'hi mostra és visible. Per aquest motiu, ves amb compte amb elements com les contrasenyes, les dades de pagament, els missatges, les fotos, i l\'àudio i el vídeo."</string> + <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="7487834861348460736">"Quan emets una aplicació, qualsevol cosa que s\'hi mostra o reprodueix és visible. Per aquest motiu, ves amb compte amb elements com les contrasenyes, les dades de pagament, els missatges, les fotos, i l\'àudio i el vídeo."</string> <string name="media_projection_entry_cast_permission_dialog_continue_entire_screen" msgid="3261124185304676483">"Emet la pantalla"</string> <string name="media_projection_entry_cast_app_selector_title" msgid="6323062146661922387">"Tria una aplicació per emetre"</string> <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"Vols començar a compartir?"</string> @@ -698,8 +702,7 @@ <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Fix"</string> <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Seguiment del cap"</string> <string name="volume_ringer_change" msgid="3574969197796055532">"Toca per canviar el mode de timbre"</string> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"mode de timbre"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"silenciar"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"deixar de silenciar"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrar"</string> @@ -869,6 +872,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Canvia a l\'aplicació de la dreta o de sota amb la pantalla dividida"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Canvia a l\'aplicació de l\'esquerra o de dalt amb la pantalla dividida"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"Durant el mode de pantalla dividida: substitueix una app per una altra"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Entrada"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"Canvia a l\'idioma següent"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"Caniva a l\'idioma anterior"</string> @@ -1113,7 +1118,7 @@ <string name="accessibility_control_change_unfavorite" msgid="6997408061750740327">"suprimir dels preferits"</string> <string name="accessibility_control_move" msgid="8980344493796647792">"Mou a la posició <xliff:g id="NUMBER">%d</xliff:g>"</string> <string name="controls_favorite_default_title" msgid="967742178688938137">"Controls"</string> - <string name="controls_favorite_subtitle" msgid="5818709315630850796">"Tria a quins controls del dispositiu vols accedir ràpidament"</string> + <string name="controls_favorite_subtitle" msgid="5818709315630850796">"Tria a quins controls de dispositius vols accedir ràpidament"</string> <string name="controls_favorite_rearrange" msgid="5616952398043063519">"Mantén premuts els controls i arrossega\'ls per reordenar-los"</string> <string name="controls_favorite_removed" msgid="5276978408529217272">"S\'han suprimit tots els controls"</string> <string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"Els canvis no s\'han desat"</string> @@ -1124,7 +1129,7 @@ <string name="controls_favorite_load_error" msgid="5126216176144877419">"No s\'han pogut carregar els controls. Consulta l\'aplicació <xliff:g id="APP">%s</xliff:g> per assegurar-te que la configuració de l\'aplicació no hagi canviat."</string> <string name="controls_favorite_load_none" msgid="7687593026725357775">"Els controls compatibles no estan disponibles"</string> <string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"Altres"</string> - <string name="controls_dialog_title" msgid="2343565267424406202">"Afegeix als controls de dispositius"</string> + <string name="controls_dialog_title" msgid="2343565267424406202">"Afegeix als controls del dispositiu"</string> <string name="controls_dialog_ok" msgid="2770230012857881822">"Afegeix"</string> <string name="controls_dialog_remove" msgid="3775288002711561936">"Suprimeix"</string> <string name="controls_dialog_message" msgid="342066938390663844">"Suggerit per <xliff:g id="APP">%s</xliff:g>"</string> @@ -1411,15 +1416,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Personalitza"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Fet"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Desplega la icona"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"o"</string> <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> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <skip /> <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> @@ -1427,7 +1446,7 @@ <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Navega amb el teclat i el ratolí tàctil"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Aprèn els gestos del ratolí tàctil, les tecles de drecera i més"</string> <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Torna"</string> - <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Ves a la pàgina d\'inici"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Ves a la pantalla d\'inici"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Mostra les aplicacions recents"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Fet"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Torna"</string> diff --git a/packages/SystemUI/res/values-ca/tiles_states_strings.xml b/packages/SystemUI/res/values-ca/tiles_states_strings.xml index 67eb853c9ee6..ea1a576d2cc0 100644 --- a/packages/SystemUI/res/values-ca/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-ca/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"Desactivat"</item> <item msgid="3028994095749238254">"Activat"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml index bc52bb81a81a..3f00ce42939a 100644 --- a/packages/SystemUI/res/values-cs/strings.xml +++ b/packages/SystemUI/res/values-cs/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Vstup"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Naslouchátka"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Zapínání…"</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Autom. otáčení"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Automatické otáčení obrazovky"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Poloha"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"Předvolbu nelze aktualizovat"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Předvolba"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Okamžité titulky"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Odblokovat mikrofon zařízení?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Odblokovat fotoaparát zařízení?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Odblokovat fotoaparát a mikrofon zařízení?"</string> @@ -574,7 +578,7 @@ <string name="media_projection_task_switcher_notification_channel" msgid="7613206306777814253">"Přepnutí aplikace"</string> <string name="screen_capturing_disabled_by_policy_dialog_title" msgid="2113331792064527203">"Blokováno administrátorem IT"</string> <string name="screen_capturing_disabled_by_policy_dialog_description" msgid="6015975736747696431">"Záznam obrazovky je zakázán zásadami zařízení"</string> - <string name="clear_all_notifications_text" msgid="348312370303046130">"Smazat vše"</string> + <string name="clear_all_notifications_text" msgid="348312370303046130">"Vymazat vše"</string> <string name="manage_notifications_text" msgid="6885645344647733116">"Spravovat"</string> <string name="manage_notifications_history_text" msgid="57055985396576230">"Historie"</string> <string name="notification_settings_button_description" msgid="2441994740884163889">"Nastavení oznámení"</string> @@ -698,8 +702,7 @@ <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Fixovaný"</string> <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Sledování hlavy"</string> <string name="volume_ringer_change" msgid="3574969197796055532">"Klepnutím změníte režim vyzvánění"</string> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"režim vyzvánění"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"vypnout zvuk"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"zapnout zvuk"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrovat"</string> @@ -869,6 +872,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Přepnout na aplikaci vpravo nebo dole v režimu rozdělené obrazovky"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Přepnout na aplikaci vlevo nebo nahoře v režimu rozdělené obrazovky"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"V režimu rozdělené obrazovky: nahradit jednu aplikaci druhou"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Vstup"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"Přepnout na další jazyk"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"Přepnout na předchozí jazyk"</string> @@ -1411,15 +1416,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Přizpůsobit"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Hotovo"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona rozbalení"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"nebo"</string> <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> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <skip /> <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> @@ -1433,11 +1452,11 @@ <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Zpět"</string> <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Přejeďte po touchpadu třemi prsty doleva nebo doprava"</string> <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Skvělé!"</string> - <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Dokončili jste gesto pro přechod zpět."</string> + <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Provedli jste gesto pro přechod zpět."</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Přejít na plochu"</string> <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Přejeďte po touchpadu třemi prsty nahoru"</string> <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Výborně!"</string> - <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Dokončili jste gesto pro přechod na plochu"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Provedli jste gesto pro přechod na plochu"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Zobrazit nedávné aplikace"</string> <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Přejeďte po touchpadu třemi prsty nahoru a podržte je"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Výborně!"</string> @@ -1445,7 +1464,7 @@ <string name="tutorial_action_key_title" msgid="8172535792469008169">"Zobrazit všechny aplikace"</string> <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Stiskněte akční klávesu na klávesnici"</string> <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Výborně!"</string> - <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Dokončili jste gesto k zobrazení všech aplikací"</string> + <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Provedli jste gesto k zobrazení všech aplikací"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Podsvícení klávesnice"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Úroveň %1$d z %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Ovládání domácnosti"</string> diff --git a/packages/SystemUI/res/values-cs/tiles_states_strings.xml b/packages/SystemUI/res/values-cs/tiles_states_strings.xml index ae533a8623ec..abfe50df0d36 100644 --- a/packages/SystemUI/res/values-cs/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-cs/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"Vypnuto"</item> <item msgid="3028994095749238254">"Zapnuto"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml index 0da56bd0f1dc..9229b18d3363 100644 --- a/packages/SystemUI/res/values-da/strings.xml +++ b/packages/SystemUI/res/values-da/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Input"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Høreapparater"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Aktiverer…"</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Roter automatisk"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Roter skærmen automatisk"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Lokation"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"Forindstillingen kunne ikke opdateres"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Forindstilling"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Livetekstning"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Vil du fjerne blokeringen af enhedens mikrofon?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Vil du fjerne blokeringen af enhedens kamera?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Vil du fjerne blokeringen af enhedens kamera og mikrofon?"</string> @@ -698,8 +702,7 @@ <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Fast"</string> <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Register. af hovedbevægelser"</string> <string name="volume_ringer_change" msgid="3574969197796055532">"Tryk for at ændre ringetilstand"</string> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"ringetilstand"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"slå lyden fra"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"slå lyden til"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrer"</string> @@ -869,6 +872,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Skift til en app til højre eller nedenfor, når du bruger opdelt skærm"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Skift til en app til venstre eller ovenfor, når du bruger opdelt skærm"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"Ved opdelt skærm: Udskift én app med en anden"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Input"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"Skift til næste sprog"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"Skift til forrige sprog"</string> @@ -1411,15 +1416,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Tilpas"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Udfør"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikon for Udvid"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"eller"</string> <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> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <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> @@ -1433,7 +1452,7 @@ <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Gå tilbage"</string> <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Stryg til venstre eller højre med tre fingre på touchpladen"</string> <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Sådan!"</string> - <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Du har fuldført bevægelsen for Gå tilbage."</string> + <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Du har udført bevægelsen for Gå tilbage."</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Gå til startskærmen"</string> <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Stryg opad med tre fingre på touchpladen"</string> <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Flot!"</string> diff --git a/packages/SystemUI/res/values-da/tiles_states_strings.xml b/packages/SystemUI/res/values-da/tiles_states_strings.xml index 2c3b0535cb33..9009eedc39cc 100644 --- a/packages/SystemUI/res/values-da/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-da/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"Fra"</item> <item msgid="3028994095749238254">"Til"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml index f9b844d73fe2..e8dcae9828ce 100644 --- a/packages/SystemUI/res/values-de/strings.xml +++ b/packages/SystemUI/res/values-de/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Eingabe"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Hörgerät"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Wird aktiviert…"</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Autom. drehen"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Bildschirm automatisch drehen"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Standort"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"Voreinstellung konnte nicht aktualisiert werden"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Voreinstellung"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Automatische Untertitel"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Blockierung des Gerätemikrofons aufheben?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Blockierung der Gerätekamera aufheben?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Blockierung von Gerätekamera und Gerätemikrofon aufheben?"</string> @@ -698,8 +702,7 @@ <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Statisch"</string> <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Erfassung von Kopfbewegungen"</string> <string name="volume_ringer_change" msgid="3574969197796055532">"Zum Ändern des Klingeltonmodus tippen"</string> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"Klingeltonmodus"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"Stummschalten"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"Aufheben der Stummschaltung"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"Vibrieren lassen"</string> @@ -869,6 +872,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Im Splitscreen-Modus zu einer App rechts oder unten wechseln"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Im Splitscreen-Modus zu einer App links oder oben wechseln"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"Im Splitscreen: eine App durch eine andere ersetzen"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Eingabe"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"Zur nächsten Sprache wechseln"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"Zur vorherigen Sprache wechseln"</string> @@ -1411,15 +1416,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Anpassen"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Fertig"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Symbol „Maximieren“"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"oder"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Ziehpunkt"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Tastatureinstellungen"</string> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <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> @@ -1427,7 +1446,7 @@ <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Navigation mit Tastatur und Touchpad"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Informationen zu Touchpad-Gesten, Tastenkombinationen und mehr"</string> <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Zurück"</string> - <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Zur Startseite"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Zum Startbildschirm"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Letzte Apps aufrufen"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Fertig"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Zurück"</string> diff --git a/packages/SystemUI/res/values-de/tiles_states_strings.xml b/packages/SystemUI/res/values-de/tiles_states_strings.xml index 0606cc79f2e9..e7f5b574755a 100644 --- a/packages/SystemUI/res/values-de/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-de/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"Aus"</item> <item msgid="3028994095749238254">"An"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml index 74019de1886b..cf9adddcc3ea 100644 --- a/packages/SystemUI/res/values-el/strings.xml +++ b/packages/SystemUI/res/values-el/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Είσοδος"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Βοηθήματα ακοής"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Ενεργοποίηση…"</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Αυτόματη περιστροφή"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Αυτόματη περιστροφή οθόνης"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Τοποθεσία"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"Δεν ήταν δυνατή η ενημέρωση της προεπιλογής"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Προεπιλογή"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Ζωντανοί υπότιτλοι"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Κατάργηση αποκλεισμού μικροφώνου συσκευής;"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Κατάργηση αποκλεισμού κάμερας συσκευής;"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Κατάργηση αποκλεισμού κάμερας και μικροφώνου συσκευής;"</string> @@ -589,8 +593,7 @@ <string name="media_projection_action_text" msgid="3634906766918186440">"Έναρξη τώρα"</string> <string name="empty_shade_text" msgid="8935967157319717412">"Δεν υπάρχουν ειδοποιήσεις"</string> <string name="no_unseen_notif_text" msgid="395512586119868682">"Δεν υπάρχουν νέες ειδοποιήσεις"</string> - <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) --> - <skip /> + <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"Η ρύθμιση cooldown ειδοποιήσεων είναι πλέον ενεργή"</string> <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Αυτόματη μείωση έντασης ήχου συσκευής και ειδοποιήσεων για έως 2 λεπτά όταν λαμβάνετε πολλές ειδοποιήσεις ταυτόχρονα."</string> <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Απενεργοποίηση"</string> <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Ξεκλειδώστε για εμφάνιση παλαιότ. ειδοπ."</string> @@ -868,6 +871,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Εναλλαγή στην εφαρμογή δεξιά ή κάτω κατά τη χρήση διαχωρισμού οθόνης"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Εναλλαγή σε εφαρμογή αριστερά ή επάνω κατά τη χρήση διαχωρισμού οθόνης"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"Κατά τον διαχωρισμό οθόνης: αντικατάσταση μιας εφαρμογής με άλλη"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Είσοδος"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"Εναλλαγή στην επόμενη γλώσσα"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"Εναλλαγή στην προηγούμενη γλώσσα"</string> @@ -1410,15 +1415,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Προσαρμογή"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Τέλος"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Εικονίδιο ανάπτυξης"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ή"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Λαβή μεταφοράς"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Ρυθμίσεις πληκτρολογίου"</string> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <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-el/tiles_states_strings.xml b/packages/SystemUI/res/values-el/tiles_states_strings.xml index d4545ffa6641..1276fb4addbc 100644 --- a/packages/SystemUI/res/values-el/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-el/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"Ανενεργή"</item> <item msgid="3028994095749238254">"Ενεργή"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml index d56abeb98050..66659599dc94 100644 --- a/packages/SystemUI/res/values-en-rAU/strings.xml +++ b/packages/SystemUI/res/values-en-rAU/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Input"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Hearing aids"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Turning on…"</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Auto-rotate"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Auto-rotate screen"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Location"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"Couldn\'t update preset"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Preset"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Live Caption"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Unblock device microphone?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Unblock device camera?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Unblock device camera and microphone?"</string> @@ -589,8 +593,7 @@ <string name="media_projection_action_text" msgid="3634906766918186440">"Start now"</string> <string name="empty_shade_text" msgid="8935967157319717412">"No notifications"</string> <string name="no_unseen_notif_text" msgid="395512586119868682">"No new notifications"</string> - <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) --> - <skip /> + <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"Notification cooldown is now on"</string> <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Your device volume and alerts are reduced automatically for up to 2 minutes when you get too many notifications at once."</string> <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Turn off"</string> <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Unlock to see older notifications"</string> @@ -698,8 +701,7 @@ <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Fixed"</string> <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Head tracking"</string> <string name="volume_ringer_change" msgid="3574969197796055532">"Tap to change ringer mode"</string> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"ringer mode"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"mute"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"unmute"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrate"</string> @@ -869,6 +871,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Switch to the app on the right or below while using split screen"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Switch to the app on the left or above while using split screen"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"During split screen: Replace an app from one to another"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Input"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"Switch to next language"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"Switch to previous language"</string> @@ -1411,15 +1415,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Customise"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Done"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Expand icon"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"or"</string> <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> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <skip /> <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-rAU/tiles_states_strings.xml b/packages/SystemUI/res/values-en-rAU/tiles_states_strings.xml index 39dd7c84b13e..c0bbabea4d78 100644 --- a/packages/SystemUI/res/values-en-rAU/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-en-rAU/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"Off"</item> <item msgid="3028994095749238254">"On"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml index bee2393b4080..9a3bedaabe4c 100644 --- a/packages/SystemUI/res/values-en-rCA/strings.xml +++ b/packages/SystemUI/res/values-en-rCA/strings.xml @@ -326,6 +326,7 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Input"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Hearing aids"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Turning on…"</string> + <string name="quick_settings_brightness_unable_adjust_msg" msgid="786478497970492300">"Can\'t adjust brightness because it\'s being\n controlled by the top app"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Auto-rotate"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Auto-rotate screen"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Location"</string> @@ -414,6 +415,7 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"Couldn\'t update preset"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Preset"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Live Caption"</string> + <string name="quick_settings_notes_label" msgid="1028004078001002623">"Note"</string> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Unblock device microphone?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Unblock device camera?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Unblock device camera and microphone?"</string> @@ -589,8 +591,7 @@ <string name="media_projection_action_text" msgid="3634906766918186440">"Start now"</string> <string name="empty_shade_text" msgid="8935967157319717412">"No notifications"</string> <string name="no_unseen_notif_text" msgid="395512586119868682">"No new notifications"</string> - <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) --> - <skip /> + <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"Notification cooldown is now on"</string> <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Your device volume and alerts are reduced automatically for up to 2 minutes when you get too many notifications at once."</string> <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Turn off"</string> <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Unlock to see older notifications"</string> @@ -868,6 +869,7 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Switch to app on right or below while using split screen"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Switch to app on left or above while using split screen"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"During split screen: replace an app from one to another"</string> + <string name="system_multitasking_move_to_next_display" msgid="6169737557526976997">"Move active window between displays"</string> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Input"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"Switch to next language"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"Switch to previous language"</string> @@ -1410,15 +1412,22 @@ <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> <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Search shortcuts"</string> <string name="shortcut_helper_no_search_results" msgid="8554756497996692160">"No search results"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Collapse icon"</string> + <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Action or Meta key icon"</string> + <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Plus icon"</string> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Customize"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Done"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Expand icon"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"or"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Drag handle"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Keyboard Settings"</string> + <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Set shortcut"</string> + <string name="shortcut_helper_customize_dialog_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="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/tiles_states_strings.xml b/packages/SystemUI/res/values-en-rCA/tiles_states_strings.xml index 39dd7c84b13e..1b60921d3237 100644 --- a/packages/SystemUI/res/values-en-rCA/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-en-rCA/tiles_states_strings.xml @@ -191,4 +191,9 @@ <item msgid="3079622119444911877">"Off"</item> <item msgid="3028994095749238254">"On"</item> </string-array> + <string-array name="tile_states_notes"> + <item msgid="5894333929299989301">"Unavailable"</item> + <item msgid="6419996398343291862">"Off"</item> + <item msgid="5908720590832378783">"On"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml index d56abeb98050..66659599dc94 100644 --- a/packages/SystemUI/res/values-en-rGB/strings.xml +++ b/packages/SystemUI/res/values-en-rGB/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Input"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Hearing aids"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Turning on…"</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Auto-rotate"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Auto-rotate screen"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Location"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"Couldn\'t update preset"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Preset"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Live Caption"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Unblock device microphone?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Unblock device camera?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Unblock device camera and microphone?"</string> @@ -589,8 +593,7 @@ <string name="media_projection_action_text" msgid="3634906766918186440">"Start now"</string> <string name="empty_shade_text" msgid="8935967157319717412">"No notifications"</string> <string name="no_unseen_notif_text" msgid="395512586119868682">"No new notifications"</string> - <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) --> - <skip /> + <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"Notification cooldown is now on"</string> <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Your device volume and alerts are reduced automatically for up to 2 minutes when you get too many notifications at once."</string> <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Turn off"</string> <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Unlock to see older notifications"</string> @@ -698,8 +701,7 @@ <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Fixed"</string> <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Head tracking"</string> <string name="volume_ringer_change" msgid="3574969197796055532">"Tap to change ringer mode"</string> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"ringer mode"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"mute"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"unmute"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrate"</string> @@ -869,6 +871,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Switch to the app on the right or below while using split screen"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Switch to the app on the left or above while using split screen"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"During split screen: Replace an app from one to another"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Input"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"Switch to next language"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"Switch to previous language"</string> @@ -1411,15 +1415,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Customise"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Done"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Expand icon"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"or"</string> <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> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <skip /> <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/tiles_states_strings.xml b/packages/SystemUI/res/values-en-rGB/tiles_states_strings.xml index 39dd7c84b13e..c0bbabea4d78 100644 --- a/packages/SystemUI/res/values-en-rGB/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-en-rGB/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"Off"</item> <item msgid="3028994095749238254">"On"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml index d56abeb98050..66659599dc94 100644 --- a/packages/SystemUI/res/values-en-rIN/strings.xml +++ b/packages/SystemUI/res/values-en-rIN/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Input"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Hearing aids"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Turning on…"</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Auto-rotate"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Auto-rotate screen"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Location"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"Couldn\'t update preset"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Preset"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Live Caption"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Unblock device microphone?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Unblock device camera?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Unblock device camera and microphone?"</string> @@ -589,8 +593,7 @@ <string name="media_projection_action_text" msgid="3634906766918186440">"Start now"</string> <string name="empty_shade_text" msgid="8935967157319717412">"No notifications"</string> <string name="no_unseen_notif_text" msgid="395512586119868682">"No new notifications"</string> - <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) --> - <skip /> + <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"Notification cooldown is now on"</string> <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Your device volume and alerts are reduced automatically for up to 2 minutes when you get too many notifications at once."</string> <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Turn off"</string> <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Unlock to see older notifications"</string> @@ -698,8 +701,7 @@ <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Fixed"</string> <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Head tracking"</string> <string name="volume_ringer_change" msgid="3574969197796055532">"Tap to change ringer mode"</string> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"ringer mode"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"mute"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"unmute"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrate"</string> @@ -869,6 +871,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Switch to the app on the right or below while using split screen"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Switch to the app on the left or above while using split screen"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"During split screen: Replace an app from one to another"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Input"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"Switch to next language"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"Switch to previous language"</string> @@ -1411,15 +1415,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Customise"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Done"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Expand icon"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"or"</string> <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> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <skip /> <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/tiles_states_strings.xml b/packages/SystemUI/res/values-en-rIN/tiles_states_strings.xml index 39dd7c84b13e..c0bbabea4d78 100644 --- a/packages/SystemUI/res/values-en-rIN/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-en-rIN/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"Off"</item> <item msgid="3028994095749238254">"On"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml index 6f04e8ea6dba..3dc305811498 100644 --- a/packages/SystemUI/res/values-es-rUS/strings.xml +++ b/packages/SystemUI/res/values-es-rUS/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Entrada"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Audífonos"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Activando…"</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Giro automático"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Girar la pantalla automáticamente"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Ubicación"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"No se pudo actualizar el ajuste predeterminado"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Ajuste predeterminado"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Subtitulado instantáneo"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"¿Quieres desbloquear el micrófono del dispositivo?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"¿Quieres desbloquear la cámara del dispositivo?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"¿Quieres desbloquear la cámara y el micrófono del dispositivo?"</string> @@ -589,8 +593,7 @@ <string name="media_projection_action_text" msgid="3634906766918186440">"Comenzar ahora"</string> <string name="empty_shade_text" msgid="8935967157319717412">"No hay notificaciones"</string> <string name="no_unseen_notif_text" msgid="395512586119868682">"No hay notificaciones nuevas"</string> - <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) --> - <skip /> + <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"Reducción de sonido de notificaciones ahora está activada"</string> <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"El volumen y las alertas se reducen por hasta 2 minutos si recibes muchas notificaciones a la vez."</string> <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Desactivar"</string> <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Desbloquea para ver notificaciones anteriores"</string> @@ -698,8 +701,7 @@ <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Fijar"</string> <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Monitoreo de cabeza"</string> <string name="volume_ringer_change" msgid="3574969197796055532">"Presiona para cambiar el modo de timbre"</string> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"modo de timbre"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"silenciar"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"dejar de silenciar"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrar"</string> @@ -869,6 +871,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Ubicar la app a la derecha o abajo cuando usas la pantalla dividida"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Ubicar la app a la izquierda o arriba cuando usas la pantalla dividida"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"Durante pantalla dividida: Reemplaza una app con otra"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Entrada"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"Cambiar al próximo idioma"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"Cambiar al idioma anterior"</string> @@ -1411,15 +1415,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Personalizar"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Listo"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ícono de expandir"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"o"</string> <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> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <skip /> <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> @@ -1427,7 +1445,7 @@ <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Navega con el teclado y el panel táctil"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Aprende sobre los gestos del panel táctil, las combinaciones de teclas y mucho más"</string> <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Atrás"</string> - <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Ir a la página de inicio"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Ir a la página principal"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Ver apps recientes"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Listo"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Atrás"</string> @@ -1439,7 +1457,7 @@ <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"¡Bien hecho!"</string> <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Completaste el gesto para ir a la página principal"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Ver apps recientes"</string> - <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Desliza hacia arriba con tres dedos en el panel táctil y mantenlos presionados."</string> + <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Desliza hacia arriba con tres dedos en el panel táctil y mantenlos presionados"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"¡Bien hecho!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Completaste el gesto para ver las apps recientes."</string> <string name="tutorial_action_key_title" msgid="8172535792469008169">"Ver todas las apps"</string> diff --git a/packages/SystemUI/res/values-es-rUS/tiles_states_strings.xml b/packages/SystemUI/res/values-es-rUS/tiles_states_strings.xml index 869efff07bdf..dec68dae3dc1 100644 --- a/packages/SystemUI/res/values-es-rUS/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-es-rUS/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"Desactivados"</item> <item msgid="3028994095749238254">"Activados"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml index 10f3754c208a..4660d9879e76 100644 --- a/packages/SystemUI/res/values-es/strings.xml +++ b/packages/SystemUI/res/values-es/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Entrada"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Audífonos"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Activando…"</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Giro automático"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Girar pantalla automáticamente"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Ubicación"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"No se ha podido actualizar el preajuste"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Preajuste"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Subtítulos automáticos"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"¿Desbloquear el micrófono del dispositivo?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"¿Desbloquear la cámara del dispositivo?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"¿Desbloquear la cámara y el micrófono del dispositivo?"</string> @@ -698,8 +702,7 @@ <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Fijo"</string> <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Seguimiento de cabeza"</string> <string name="volume_ringer_change" msgid="3574969197796055532">"Toca para cambiar el modo de timbre"</string> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"modo de timbre"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"silenciar"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"dejar de silenciar"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrar"</string> @@ -869,6 +872,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Cambiar a la aplicación de la derecha o de abajo en pantalla dividida"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Cambiar a la app de la izquierda o de arriba en pantalla dividida"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"Con pantalla dividida: reemplazar una aplicación por otra"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Entrada"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"Cambiar a siguiente idioma"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"Cambiar a idioma anterior"</string> @@ -1411,15 +1416,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Personalizar"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Hecho"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icono de desplegar"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"o"</string> <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> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <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-es/tiles_states_strings.xml b/packages/SystemUI/res/values-es/tiles_states_strings.xml index 08644e1cbe40..e872c263f1e6 100644 --- a/packages/SystemUI/res/values-es/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-es/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"Desactivados"</item> <item msgid="3028994095749238254">"Activado"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml index e73a7dd9e23b..f2a92f299081 100644 --- a/packages/SystemUI/res/values-et/strings.xml +++ b/packages/SystemUI/res/values-et/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Sisend"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Kuuldeaparaadid"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Sisselülitamine …"</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Autom. pööramine"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Kuva automaatne pööramine"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Asukoht"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"Eelseadistust ei saanud värskendada"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Eelseadistus"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Reaalajas subtiitrid"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Kas tühistada seadme mikrofoni blokeerimine?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Kas tühistada seadme kaamera blokeerimine?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Kas tühistada seadme kaamera ja mikrofoni blokeerimine?"</string> @@ -698,8 +702,7 @@ <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Fikseeritud"</string> <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Pea jälgimine"</string> <string name="volume_ringer_change" msgid="3574969197796055532">"Puudutage telefonihelina režiimi muutmiseks"</string> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"telefonihelina režiim"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"vaigistamine"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"vaigistuse tühistamine"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibreerimine"</string> @@ -869,6 +872,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Paremale või alumisele rakendusele lülitamine jagatud ekraani ajal"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Vasakule või ülemisele rakendusele lülitamine jagatud ekraani ajal"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"Ekraanikuva jagamise ajal: ühe rakenduse asendamine teisega"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Sisend"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"Järgmisele keelele lülitamine"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"Eelmisele keelele lülitamine"</string> @@ -1411,15 +1416,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Kohandamine"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Valmis"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Laiendamisikoon"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"või"</string> <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> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <skip /> <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-et/tiles_states_strings.xml b/packages/SystemUI/res/values-et/tiles_states_strings.xml index 704649e77b86..3af8dea15541 100644 --- a/packages/SystemUI/res/values-et/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-et/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"Välja lülitatud"</item> <item msgid="3028994095749238254">"Sisse lülitatud"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml index 71bc77f0572b..daca2a37635e 100644 --- a/packages/SystemUI/res/values-eu/strings.xml +++ b/packages/SystemUI/res/values-eu/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Sarrera"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Audifonoak"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Aktibatzen…"</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Biratze automatikoa"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Biratu pantaila automatikoki"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Kokapena"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"Ezin izan da eguneratu aurrezarpena"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Aurrezarpena"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Istanteko azpitituluak"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Gailuaren mikrofonoa desblokeatu nahi duzu?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Gailuaren kamera desblokeatu nahi duzu?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Gailuaren kamera eta mikrofonoa desblokeatu nahi dituzu?"</string> @@ -698,8 +702,7 @@ <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Finkoa"</string> <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Buruaren jarraipena"</string> <string name="volume_ringer_change" msgid="3574969197796055532">"Sakatu tonu-jotzailearen modua aldatzeko"</string> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"tonu-jotzailearen modua"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"desaktibatu audioa"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"aktibatu audioa"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"dardara"</string> @@ -869,6 +872,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Aldatu eskuineko edo beheko aplikaziora pantaila zatitua erabiltzean"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Aldatu ezkerreko edo goiko aplikaziora pantaila zatitua erabiltzean"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"Pantaila zatituan zaudela, ordeztu aplikazio bat beste batekin"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Sarrera"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"Aldatu hurrengo hizkuntzara"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"Aldatu aurreko hizkuntzara"</string> @@ -1411,15 +1416,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Pertsonalizatu"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Eginda"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Zabaltzeko ikonoa"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"edo"</string> <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> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <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> @@ -1460,8 +1479,8 @@ <string name="contextual_education_dialog_title" msgid="4630392552837487324">"Testuinguruaren araberako hezkuntza"</string> <string name="back_edu_notification_title" msgid="5624780717751357278">"Erabili ukipen-panela atzera egiteko"</string> <string name="back_edu_notification_content" msgid="2497557451540954068">"Pasatu 3 hatz ezkerrera edo eskuinera. Sakatu keinu gehiago ikasteko."</string> - <string name="home_edu_notification_title" msgid="6097902076909654045">"Erabili ukipen-panela hasierako pantailara joateko"</string> - <string name="home_edu_notification_content" msgid="6631697734535766588">"Pasatu 3 hatz. Sakatu keinu gehiago ikasteko."</string> + <string name="home_edu_notification_title" msgid="6097902076909654045">"Erabili ukipen-panela orri nagusira joateko"</string> + <string name="home_edu_notification_content" msgid="6631697734535766588">"Pasatu 3 hatz gora. Sakatu keinu gehiago ikasteko."</string> <string name="overview_edu_notification_title" msgid="1265824157319562406">"Erabili ukipen-panela azkenaldiko aplikazioak ikusteko"</string> <string name="overview_edu_notification_content" msgid="3578204677648432500">"Pasatu 3 hatz gora eta eduki sakatuta. Sakatu keinu gehiago ikasteko."</string> <string name="all_apps_edu_notification_title" msgid="372262997265569063">"Erabili teklatua aplikazio guztiak ikusteko"</string> diff --git a/packages/SystemUI/res/values-eu/tiles_states_strings.xml b/packages/SystemUI/res/values-eu/tiles_states_strings.xml index 13e14e0502c4..8ada72a9f3ae 100644 --- a/packages/SystemUI/res/values-eu/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-eu/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"Desaktibatuta"</item> <item msgid="3028994095749238254">"Aktibatuta"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml index d82edf90a990..4f2c89e57216 100644 --- a/packages/SystemUI/res/values-fa/strings.xml +++ b/packages/SystemUI/res/values-fa/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"ورودی"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"سمعک"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"روشن کردن…"</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"چرخش خودکار"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"چرخش خودکار صفحهنمایش"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"مکان"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"پیشتنظیم بهروزرسانی نشد"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"پیشتنظیم"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"زیرنویس زنده ناشنوایان"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"میکروفون دستگاه لغو انسداد شود؟"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"دوربین دستگاه لغو انسداد شود؟"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"دوربین و میکروفون دستگاه لغو انسداد شود؟"</string> @@ -589,8 +593,7 @@ <string name="media_projection_action_text" msgid="3634906766918186440">"اکنون شروع کنید"</string> <string name="empty_shade_text" msgid="8935967157319717412">"اعلانی موجود نیست"</string> <string name="no_unseen_notif_text" msgid="395512586119868682">"اعلان جدیدی وجود ندارد"</string> - <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) --> - <skip /> + <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"«استراحت اعلانها» اکنون روشن است"</string> <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"وقتی بهطور همزمان تعداد بسیار زیادی اعلان دریافت کنید، میزان صدای دستگاه و هشدارها بهطور خودکار تا ۲ دقیقه کاهش مییابد."</string> <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"خاموش کردن"</string> <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"برای دیدن اعلانهای قبلی قفل را باز کنید"</string> @@ -698,8 +701,7 @@ <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> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"حالت زنگ"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"صامت کردن"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"باصدا کردن"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"لرزش"</string> @@ -869,6 +871,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"رفتن به برنامه سمت راست یا پایین درحین استفاده از صفحهٔ دونیمه"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"رفتن به برنامه سمت چپ یا بالا درحین استفاده از صفحهٔ دونیمه"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"درحین صفحهٔ دونیمه: برنامهای را با دیگری جابهجا میکند"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"ورودی"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"رفتن به زبان بعدی"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"رفتن به زبان قبلی"</string> @@ -1411,15 +1415,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"سفارشیسازی کردن"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"تمام"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"نماد ازهم بازکردن"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"یا"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"دستگیره کشاندن"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"تنظیمات صفحهکلید"</string> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <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-fa/tiles_states_strings.xml b/packages/SystemUI/res/values-fa/tiles_states_strings.xml index 756b442f5fc4..b7f4830db666 100644 --- a/packages/SystemUI/res/values-fa/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-fa/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"خاموش"</item> <item msgid="3028994095749238254">"روشن"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml index e3d27a587814..581f0eada3ac 100644 --- a/packages/SystemUI/res/values-fi/strings.xml +++ b/packages/SystemUI/res/values-fi/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Syöttölaite"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Kuulolaitteet"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Otetaan käyttöön…"</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automaattinen kääntö"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Käännä näyttöä automaattisesti."</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Sijainti"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"Esiasetusta ei voitu muuttaa"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Esiasetus"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Livetekstitys"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Kumotaanko laitteen mikrofonin esto?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Kumotaanko laitteen kameran esto?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Kumotaanko laitteen kameran ja mikrofonin esto?"</string> @@ -698,8 +702,7 @@ <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Kiinteä"</string> <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Pään seuranta"</string> <string name="volume_ringer_change" msgid="3574969197796055532">"Vaihda soittoäänen tilaa napauttamalla"</string> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"Soittoäänen tila"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"mykistä"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"poista mykistys"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"värinä"</string> @@ -869,6 +872,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Vaihda sovellukseen oikealla tai alapuolella jaetussa näytössä"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Vaihda sovellukseen vasemmalla tai yläpuolella jaetussa näytössä"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"Jaetun näytön aikana: korvaa sovellus toisella"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Syöttötapa"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"Vaihda seuraavaan kieleen"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"Vaihda aiempaan kieleen"</string> @@ -1411,15 +1416,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Muokkaa"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Valmis"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Laajennuskuvake"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"tai"</string> <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> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <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> @@ -1437,7 +1456,7 @@ <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Siirry etusivulle"</string> <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Pyyhkäise ylös kolmella sormella kosketuslevyllä"</string> <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Hienoa!"</string> - <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Olet oppinut aloitusnäytölle palaamiseleen"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Olet oppinut eleen, jolla pääset takaisin aloitusnäytölle"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Katso viimeisimmät sovellukset"</string> <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Pyyhkäise ylös ja pidä kosketuslevyä painettuna kolmella sormella"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Hienoa!"</string> @@ -1445,7 +1464,7 @@ <string name="tutorial_action_key_title" msgid="8172535792469008169">"Näytä kaikki sovellukset"</string> <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Paina näppäimistön toimintonäppäintä"</string> <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Hienoa!"</string> - <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Olet oppinut Näytä kaikki sovellukset ‑eleen"</string> + <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Olet oppinut Näytä kaikki sovellukset ‑eleen."</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Näppämistön taustavalo"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Taso %1$d/%2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Kodin ohjaus"</string> diff --git a/packages/SystemUI/res/values-fi/tiles_states_strings.xml b/packages/SystemUI/res/values-fi/tiles_states_strings.xml index 5ecc95956d1c..e323b8a46bc4 100644 --- a/packages/SystemUI/res/values-fi/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-fi/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"Pois päältä"</item> <item msgid="3028994095749238254">"Päällä"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml index d01acd3de342..bae94715ae6b 100644 --- a/packages/SystemUI/res/values-fr-rCA/strings.xml +++ b/packages/SystemUI/res/values-fr-rCA/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Entrée"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Prothèses auditives"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Activation en cours…"</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Rotation auto"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Rotation automatique de l\'écran"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Localisation"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"Impossible de mettre à jour le préréglage"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Préréglage"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Sous-titres instantanés"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Débloquer le microphone de l\'appareil?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Débloquer l\'appareil photo de l\'appareil?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Débloquer l\'appareil photo et le microphone?"</string> @@ -589,8 +593,7 @@ <string name="media_projection_action_text" msgid="3634906766918186440">"Commencer"</string> <string name="empty_shade_text" msgid="8935967157319717412">"Aucune notification"</string> <string name="no_unseen_notif_text" msgid="395512586119868682">"Aucune nouvelle notification"</string> - <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) --> - <skip /> + <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"L\'atténuation des notifications est maintenant activée"</string> <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Les alertes et le volume de l\'appareil sont réduits automatiquement pendant 2 minutes maximum quand vous recevez trop de notifications à la fois."</string> <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Désactiver"</string> <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Déverr. pour voir les anciennes notif."</string> @@ -698,8 +701,7 @@ <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Activé"</string> <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Suivi de la tête"</string> <string name="volume_ringer_change" msgid="3574969197796055532">"Touchez pour modifier le mode de sonnerie"</string> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"mode de sonnerie"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"désactiver le son"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"réactiver le son"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibration"</string> @@ -869,6 +871,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Passer à l\'appli à droite ou en dessous avec l\'Écran divisé"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Passer à l\'appli à gauche ou au-dessus avec l\'Écran divisé"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"En mode d\'écran divisé : remplacer une appli par une autre"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Entrée"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"Passer à la langue suivante"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"Passer à la langue précédente"</string> @@ -1411,15 +1415,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Personnaliser"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Terminé"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icône Développer"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string> <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> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <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> @@ -1469,7 +1487,7 @@ <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"La réduction supplémentaire de la luminosité fait désormais partie du curseur de luminosité"</string> <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"Vous pouvez désormais réduire la luminosité de l\'écran encore plus.\n\nÉtant donné que cette fonctionnalité fait maintenant partie du curseur de luminosité, les raccourcis de la réduction supplémentaire de la luminosité sont retirés."</string> <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"Retirer les raccourcis de la réduction supplémentaire de la luminosité"</string> - <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"Les raccourcis de la réduction supplémentaire de la luminosité ont été retirés"</string> + <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"Raccourcis de la réduction supplémentaire de la luminosité retirés"</string> <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"Connectivité"</string> <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"Accessibilité"</string> <string name="qs_edit_mode_category_utilities" msgid="8123080090108420095">"Utilitaires"</string> diff --git a/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml b/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml index f12634faecbb..0bbacd09259c 100644 --- a/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"Désactivé"</item> <item msgid="3028994095749238254">"Activé"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml index 2da6399949b3..2006ea6d0348 100644 --- a/packages/SystemUI/res/values-fr/strings.xml +++ b/packages/SystemUI/res/values-fr/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Entrée"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Appareils auditifs"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Activation…"</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Rotation auto"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Rotation automatique de l\'écran"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Localisation"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"Impossible de mettre à jour les préréglages"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Préréglage"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Sous-titres instantanés"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Débloquer le micro de l\'appareil ?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Débloquer la caméra de l\'appareil ?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Débloquer l\'appareil photo et le micro de l\'appareil ?"</string> @@ -698,8 +702,7 @@ <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Activé"</string> <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Suivi de la tête"</string> <string name="volume_ringer_change" msgid="3574969197796055532">"Appuyez pour changer le mode de la sonnerie"</string> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"mode de sonnerie"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"couper le son"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"réactiver le son"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"activer le vibreur"</string> @@ -869,6 +872,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Passer à l\'appli à droite ou en dessous avec l\'écran partagé"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Passez à l\'appli à gauche ou au-dessus avec l\'écran partagé"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"En mode écran partagé : Remplacer une appli par une autre"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Saisie"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"Passer à la langue suivante"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"Revenir à la langue précédente"</string> @@ -1411,15 +1416,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Personnaliser"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"OK"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icône Développer"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string> <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> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <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> diff --git a/packages/SystemUI/res/values-fr/tiles_states_strings.xml b/packages/SystemUI/res/values-fr/tiles_states_strings.xml index fcdd9f0e6e3a..d0853f4a3185 100644 --- a/packages/SystemUI/res/values-fr/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-fr/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"Désactivé"</item> <item msgid="3028994095749238254">"Activé"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml index e0127254bd3e..ebc19bd883f7 100644 --- a/packages/SystemUI/res/values-gl/strings.xml +++ b/packages/SystemUI/res/values-gl/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Entrada"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Audiófonos"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Activando…"</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Xirar automaticamente"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Xirar pantalla automaticamente"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Localización"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"Non se puido actualizar a configuración predeterminada"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Configuración predeterminada"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Subtítulos instantáneos"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Queres desbloquear o micrófono do dispositivo?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Queres desbloquear a cámara do dispositivo?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Queres desbloquear a cámara e o micrófono do dispositivo?"</string> @@ -698,8 +702,7 @@ <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Fixado"</string> <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Seguimento da cabeza"</string> <string name="volume_ringer_change" msgid="3574969197796055532">"Toca para cambiar o modo de timbre"</string> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"modo de timbre"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"silenciar"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"activar o son"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrar"</string> @@ -869,6 +872,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Cambiar á aplicación da dereita ou de abaixo coa pantalla dividida"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Cambiar á aplicación da esquerda ou de arriba coa pantalla dividida"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"En modo de pantalla dividida: Substituír unha aplicación por outra"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Entrada"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"Cambiar ao seguinte idioma"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"Cambiar ao idioma anterior"</string> @@ -1411,15 +1416,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Personalizar"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Feito"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icona de despregar"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string> <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> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <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> @@ -1433,11 +1452,11 @@ <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Volver"</string> <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Pasa tres dedos cara á esquerda ou cara á dereita no panel táctil"</string> <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Excelente!"</string> - <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Completaches o xesto de retroceso."</string> + <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Completaches o titorial do xesto de retroceso."</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Ir ao inicio"</string> <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Pasa tres dedos cara arriba no panel táctil"</string> - <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Excelente traballo."</string> - <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Completaches o titorial do xesto de ir ao inicio"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Ben feito!"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Completaches o titorial do xesto para ir á pantalla de inicio"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Consultar aplicacións recentes"</string> <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Pasa tres dedos cara arriba e mantenos premidos no panel táctil"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Moi ben!"</string> diff --git a/packages/SystemUI/res/values-gl/tiles_states_strings.xml b/packages/SystemUI/res/values-gl/tiles_states_strings.xml index 03b934eb9cb6..18ad3df0a8c1 100644 --- a/packages/SystemUI/res/values-gl/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-gl/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"Desactivados"</item> <item msgid="3028994095749238254">"Activados"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml index 898ca383c372..d5e7ca4177aa 100644 --- a/packages/SystemUI/res/values-gu/strings.xml +++ b/packages/SystemUI/res/values-gu/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"ઇનપુટ"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"સાંભળવામાં મદદ આપતા યંત્રો"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"ચાલુ કરી રહ્યાં છીએ…"</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ઑટો રોટેટ"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"ઑટો રોટેટ સ્ક્રીન"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"લોકેશન"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"પ્રીસેટ અપડેટ કરી શક્યા નથી"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"પ્રીસેટ"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"લાઇવ કૅપ્શન"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ડિવાઇસના માઇક્રોફોનને અનબ્લૉક કરીએ?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"ડિવાઇસના કૅમેરાને અનબ્લૉક કરીએ?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"ડિવાઇસના કૅમેરા અને માઇક્રોફોનને અનબ્લૉક કરીએ?"</string> @@ -589,8 +593,7 @@ <string name="media_projection_action_text" msgid="3634906766918186440">"હવે શરૂ કરો"</string> <string name="empty_shade_text" msgid="8935967157319717412">"કોઈ નોટિફિકેશન નથી"</string> <string name="no_unseen_notif_text" msgid="395512586119868682">"કોઈ નવું નોટિફિકેશન નથી"</string> - <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) --> - <skip /> + <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"નોટિફિકેશન કૂલડાઉનની સુવિધા ચાલુ છે"</string> <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"જ્યારે તમને એકસાથે ઘણા બધા નોટિફિકેશન મળે ત્યારે તમારા ડિવાઇસનું વૉલ્યૂમ અને અલર્ટ ઑટોમૅટિક રીતે 2 મિનિટ જેટલા સમય માટે ઘટાડવામાં આવે છે."</string> <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"બંધ કરો"</string> <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"જૂના નોટિફિકેશન જોવા માટે અનલૉક કરો"</string> @@ -698,8 +701,7 @@ <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> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"રિંગર મોડ"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"મ્યૂટ કરો"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"અનમ્યૂટ કરો"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"વાઇબ્રેટ"</string> @@ -869,6 +871,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"વિભાજિત સ્ક્રીનનો ઉપયોગ કરતી વખતે જમણી બાજુ કે નીચેની ઍપ પર સ્વિચ કરો"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"વિભાજિત સ્ક્રીનનો ઉપયોગ કરતી વખતે ડાબી બાજુની કે ઉપરની ઍપ પર સ્વિચ કરો"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"વિભાજિત સ્ક્રીન દરમિયાન: એક ઍપને બીજી ઍપમાં બદલો"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"ઇનપુટ"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"આગલી ભાષા પર સ્વિચ કરો"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"પાછલી ભાષા પર સ્વિચ કરો"</string> @@ -1411,15 +1415,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"કસ્ટમાઇઝ કરો"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"થઈ ગયું"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"\'મોટું કરો\'નું આઇકન"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"અથવા"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ઑબ્જેક્ટ ખેંચવાનું હૅન્ડલ"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"કીબોર્ડના સેટિંગ"</string> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <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-gu/tiles_states_strings.xml b/packages/SystemUI/res/values-gu/tiles_states_strings.xml index 5c4a4784c13f..e6202321148f 100644 --- a/packages/SystemUI/res/values-gu/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-gu/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"બંધ છે"</item> <item msgid="3028994095749238254">"ચાલુ છે"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml index 380d394b331a..cad756d0cebb 100644 --- a/packages/SystemUI/res/values-hi/strings.xml +++ b/packages/SystemUI/res/values-hi/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"इनपुट"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"कान की मशीनें"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"ब्लूटूथ चालू हो रहा है…"</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ऑटो-रोटेट"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"स्क्रीन का अपने-आप दिशा बदलना (ऑटो-रोटेट)"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"जगह की जानकारी"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"प्रीसेट अपडेट नहीं किया जा सका"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"प्रीसेट"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"लाइव कैप्शन"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"क्या आपको माइक्रोफ़ोन का ऐक्सेस अनब्लॉक करना है?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"क्या आपको कैमरे का ऐक्सेस अनब्लॉक करना है?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"क्या आप डिवाइस का कैमरा और माइक्रोफ़ोन अनब्लॉक करना चाहते हैं?"</string> @@ -698,8 +702,7 @@ <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> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"रिंगर मोड"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"म्यूट करें"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"अनम्यूट करें"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"वाइब्रेशन की सुविधा चालू करें"</string> @@ -869,6 +872,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"स्प्लिट स्क्रीन पर, दाईं ओर या नीचे के ऐप पर स्विच करने के लिए"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"स्प्लिट स्क्रीन पर, बाईं ओर या ऊपर के ऐप पर स्विच करने के लिए"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"स्प्लिट स्क्रीन के दौरान: एक ऐप्लिकेशन को दूसरे ऐप्लिकेशन से बदलें"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"इनपुट"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"अगली भाषा पर स्विच करने के लिए"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"पिछली भाषा पर स्विच करने के लिए"</string> @@ -1411,15 +1416,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"पसंद के मुताबिक बनाएं"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"हो गया"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"बड़ा करने का आइकॉन"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"या"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"खींचकर छोड़ने वाला हैंडल"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"कीबोर्ड सेटिंग"</string> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <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> @@ -1437,7 +1456,7 @@ <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"होम स्क्रीन पर जाएं"</string> <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"अपने टचपैड पर तीन उंगलियों से ऊपर की ओर स्वाइप करें"</string> <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"बहुत बढ़िया!"</string> - <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"अब आपको इस बारे में जानकारी है कि हाथ के जेस्चर का इस्तेमाल करके होम स्क्रीन पर कैसे जाते हैं"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"अब आपको हाथ के जेस्चर का इस्तेमाल करके होम स्क्रीन पर जाने का तरीका पता चल गया है"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"हाल ही में इस्तेमाल किए गए ऐप्लिकेशन देखें"</string> <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"अपने टचपैड पर तीन उंगलियों से ऊपर की ओर स्वाइप करें और फिर होल्ड करें"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"बहुत बढ़िया!"</string> diff --git a/packages/SystemUI/res/values-hi/tiles_states_strings.xml b/packages/SystemUI/res/values-hi/tiles_states_strings.xml index b89eeb3c0f0f..6aa90789c246 100644 --- a/packages/SystemUI/res/values-hi/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-hi/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"बंद हैं"</item> <item msgid="3028994095749238254">"चालू हैं"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml index 0a7d82be2a7b..3a01d94ef510 100644 --- a/packages/SystemUI/res/values-hr/strings.xml +++ b/packages/SystemUI/res/values-hr/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Unos"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Slušna pomagala"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Uključivanje…"</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automatsko zakretanje"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Automatsko zakretanje zaslona"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Lokacija"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"Ažuriranje unaprijed definiranih postavki nije uspjelo"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Unaprijed definirana postavka"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Automatski titlovi"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Želite li deblokirati mikrofon uređaja?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Želite li deblokirati kameru uređaja?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Želite li deblokirati kameru i mikrofon uređaja?"</string> @@ -589,8 +593,7 @@ <string name="media_projection_action_text" msgid="3634906766918186440">"Pokreni"</string> <string name="empty_shade_text" msgid="8935967157319717412">"Nema obavijesti"</string> <string name="no_unseen_notif_text" msgid="395512586119868682">"Nema novih obavijesti"</string> - <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) --> - <skip /> + <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"Stišavanje obavijesti sada je uključeno"</string> <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Glasnoća/upozorenja uređaja automatski se stišavaju do 2 min kad primite previše obavijesti odjednom"</string> <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Isključi"</string> <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Otključajte za starije obavijesti"</string> @@ -698,8 +701,7 @@ <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Fiksno"</string> <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Praćenje glave"</string> <string name="volume_ringer_change" msgid="3574969197796055532">"Dodirnite da biste promijenili način softvera zvona"</string> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"način softvera zvona"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"isključivanje zvuka"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"uključivanje zvuka"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibriranje"</string> @@ -869,6 +871,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Prelazak na aplikaciju zdesna ili ispod uz podijeljeni zaslon"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Prelazak na aplikaciju slijeva ili iznad uz podijeljeni zaslon"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"Tijekom podijeljenog zaslona: zamijeni aplikaciju drugom"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Unos"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"Prelazak na sljedeći jezik"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"Prelazak na prethodni jezik"</string> @@ -1411,15 +1415,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Prilagodi"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Gotovo"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona za proširivanje"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ili"</string> <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> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <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-hr/tiles_states_strings.xml b/packages/SystemUI/res/values-hr/tiles_states_strings.xml index df0b78664cba..3f8841afb4f2 100644 --- a/packages/SystemUI/res/values-hr/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-hr/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"Isključeno"</item> <item msgid="3028994095749238254">"Uključeno"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml index 7be9656bd082..c9e20f8359be 100644 --- a/packages/SystemUI/res/values-hu/strings.xml +++ b/packages/SystemUI/res/values-hu/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Bevitel"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Hallókészülék"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Bekapcsolás…"</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automatikus elforgatás"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Automatikus képernyőforgatás"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Tartózkodási hely"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"Nem sikerült frissíteni a beállításkészletet"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Beállításkészlet"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Élő feliratozás"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Feloldja az eszköz mikrofonjának letiltását?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Feloldja az eszköz kamerájának letiltását?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Feloldja az eszköz kamerájának és mikrofonjának letiltását?"</string> @@ -698,8 +702,7 @@ <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Rögzített"</string> <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Fejkövetés"</string> <string name="volume_ringer_change" msgid="3574969197796055532">"Koppintson a csengés módjának módosításához"</string> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"csengés módja"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"némítás"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"némítás feloldása"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"rezgés"</string> @@ -869,6 +872,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Váltás a jobb oldalt, illetve lent lévő appra osztott képernyő esetén"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Váltás a bal oldalt, illetve fent lévő appra osztott képernyő esetén"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"Osztott képernyőn: az egyik alkalmazás lecserélése egy másikra"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Bevitel"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"Váltás a következő nyelvre"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"Váltás az előző nyelvre"</string> @@ -1411,15 +1416,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Személyre szabás"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Kész"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Kibontás ikon"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"vagy"</string> <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> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <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-hu/tiles_states_strings.xml b/packages/SystemUI/res/values-hu/tiles_states_strings.xml index bbd6bc0ebbbb..76b3410ab6cb 100644 --- a/packages/SystemUI/res/values-hu/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-hu/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"Ki"</item> <item msgid="3028994095749238254">"Be"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml index c7a8a0eddb76..1d46bc2c86da 100644 --- a/packages/SystemUI/res/values-hy/strings.xml +++ b/packages/SystemUI/res/values-hy/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Մուտքագրում"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Լսողական սարք"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Միացում…"</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Ինքնապտտում"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Ավտոմատ պտտել էկրանը"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Տեղորոշում"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"Չհաջողվեց թարմացնել կարգավորումների հավաքածուն"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Կարգավորումների հավաքածու"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Կենդանի ենթագրեր"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Արգելահանե՞լ սարքի խոսափողը"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Արգելահանե՞լ սարքի տեսախցիկը"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Արգելահանե՞լ սարքի տեսախցիկը և խոսափողը"</string> @@ -589,8 +593,7 @@ <string name="media_projection_action_text" msgid="3634906766918186440">"Սկսել հիմա"</string> <string name="empty_shade_text" msgid="8935967157319717412">"Ծանուցումներ չկան"</string> <string name="no_unseen_notif_text" msgid="395512586119868682">"Նոր ծանուցումներ չկան"</string> - <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) --> - <skip /> + <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"Ծանուցումների ձայնի իջեցումը միացված է"</string> <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Սարքի և ծանուցումների ձայնն ավտոմատ իջեցվում է մինչև 2 րոպեով, երբ շատ ծանուցումներ եք ստանում։"</string> <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Անջատել"</string> <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Ապակողպեք՝ տեսնելու հին ծանուցումները"</string> @@ -698,8 +701,7 @@ <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> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"զանգակի ռեժիմ"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"անջատել ձայնը"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"միացնել ձայնը"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"միացնել թրթռոցը"</string> @@ -869,6 +871,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Անցեք աջ կողմի կամ ներքևի հավելվածին տրոհված էկրանի միջոցով"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Անցեք աջ կողմի կամ վերևի հավելվածին տրոհված էկրանի միջոցով"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"Տրոհված էկրանի ռեժիմում մեկ հավելվածը փոխարինել մյուսով"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Ներածում"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"Անցնել հաջորդ լեզվին"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"Անցնել նախորդ լեզվին"</string> @@ -1411,15 +1415,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Կարգավորել"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Պատրաստ է"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ծավալել պատկերակը"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"կամ"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Տեղափոխման նշիչ"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Ստեղնաշարի կարգավորումներ"</string> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <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-hy/tiles_states_strings.xml b/packages/SystemUI/res/values-hy/tiles_states_strings.xml index eb77ccf8c1fc..ce930c39aa83 100644 --- a/packages/SystemUI/res/values-hy/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-hy/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"Անջատված է"</item> <item msgid="3028994095749238254">"Միացված է"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml index a8a64435de2b..1535314317a8 100644 --- a/packages/SystemUI/res/values-in/strings.xml +++ b/packages/SystemUI/res/values-in/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Input"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Alat bantu dengar"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Mengaktifkan…"</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Putar Otomatis"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Putar layar otomatis"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Lokasi"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"Tidak dapat memperbarui preset"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Preset"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Teks Otomatis"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Berhenti memblokir mikrofon perangkat?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Berhenti memblokir kamera perangkat?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Berhenti memblokir kamera dan mikrofon perangkat?"</string> @@ -698,8 +702,7 @@ <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Tetap"</string> <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Pelacakan Gerak Kepala"</string> <string name="volume_ringer_change" msgid="3574969197796055532">"Ketuk untuk mengubah mode pendering"</string> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"mode pendering"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"Tanpa suara"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"aktifkan"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"getar"</string> @@ -869,6 +872,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Beralih ke aplikasi di bagian kanan atau bawah saat menggunakan layar terpisah"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Beralih ke aplikasi di bagian kiri atau atas saat menggunakan layar terpisah"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"Dalam layar terpisah: ganti salah satu aplikasi dengan yang lain"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Input"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"Beralih ke bahasa berikutnya"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"Beralih ke bahasa sebelumnya"</string> @@ -1411,15 +1416,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Sesuaikan"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Selesai"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikon luaskan"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"atau"</string> <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> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Menavigasi menggunakan keyboard"</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-in/tiles_states_strings.xml b/packages/SystemUI/res/values-in/tiles_states_strings.xml index a415f644fb48..5570edb557d4 100644 --- a/packages/SystemUI/res/values-in/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-in/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"Nonaktif"</item> <item msgid="3028994095749238254">"Aktif"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml index 66b156aba150..edead8fdc433 100644 --- a/packages/SystemUI/res/values-is/strings.xml +++ b/packages/SystemUI/res/values-is/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Inntak"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Heyrnartæki"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Kveikir…"</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Sjálfvirkur snúningur"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Snúa skjá sjálfkrafa"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Staðsetning"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"Tókst ekki að uppfæra forstillingu"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Forstilling"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Skjátextar í rauntíma"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Opna fyrir hljóðnema tækisins?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Opna fyrir myndavél tækisins?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Opna fyrir myndavél og hljóðnema tækisins?"</string> @@ -589,8 +593,7 @@ <string name="media_projection_action_text" msgid="3634906766918186440">"Byrja núna"</string> <string name="empty_shade_text" msgid="8935967157319717412">"Engar tilkynningar"</string> <string name="no_unseen_notif_text" msgid="395512586119868682">"Engar nýjar tilkynningar"</string> - <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) --> - <skip /> + <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"Nú er kveikt á tilkynningadempun"</string> <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Lækkað er sjálfkrafa í hljóðstyrk og áminningum tækisins í allt að tvær mínútur þegar þú færð of margar tilkynningar í einu."</string> <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Slökkva"</string> <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Taktu úr lás til að sjá eldri tilkynningar"</string> @@ -698,8 +701,7 @@ <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Fast"</string> <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Rakning höfuðhreyfinga"</string> <string name="volume_ringer_change" msgid="3574969197796055532">"Ýta til að skipta um hringjarastillingu"</string> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"hringistilling"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"þagga"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"hætta að þagga"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"titringur"</string> @@ -869,6 +871,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Skiptu í forrit til hægri eða fyrir neðan þegar skjáskipting er notuð"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Skiptu í forrit til vinstri eða fyrir ofan þegar skjáskipting er notuð"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"Í skjáskiptingu: Skipta forriti út fyrir annað forrit"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Innsláttur"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"Skipta yfir í næsta tungumál"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"Skipta yfir í fyrra tungumál"</string> @@ -1411,15 +1415,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Sérsníða"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Lokið"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Stækka tákn"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"eða"</string> <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> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <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-is/tiles_states_strings.xml b/packages/SystemUI/res/values-is/tiles_states_strings.xml index c9befd6574c4..893ab6ce3bb6 100644 --- a/packages/SystemUI/res/values-is/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-is/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"Slökkt"</item> <item msgid="3028994095749238254">"Kveikt"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml index 36d564ba82d4..d7a279be03ea 100644 --- a/packages/SystemUI/res/values-it/strings.xml +++ b/packages/SystemUI/res/values-it/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Ingresso"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Apparecchi acustici"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Attivazione…"</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Rotazione automatica"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Rotazione automatica dello schermo"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Posizione"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"Impossibile aggiornare preset"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Preset"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Sottotitoli in tempo reale"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Vuoi sbloccare il microfono del dispositivo?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Vuoi sbloccare la fotocamera del dispositivo?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Vuoi sbloccare la fotocamera e il microfono del dispositivo?"</string> @@ -698,8 +702,7 @@ <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Fisso"</string> <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Rilev. movim. testa"</string> <string name="volume_ringer_change" msgid="3574969197796055532">"Tocca per cambiare la modalità della suoneria"</string> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"modalità suoneria"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"silenzia"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"riattiva l\'audio"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrazione"</string> @@ -869,6 +872,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Passa all\'app a destra o sotto mentre usi lo schermo diviso"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Passa all\'app a sinistra o sopra mentre usi lo schermo diviso"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"Con lo schermo diviso: sostituisci un\'app con un\'altra"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Inserimento"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"Passa alla lingua successiva"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"Passa alla lingua precedente"</string> @@ -1411,15 +1416,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Personalizza"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Fine"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icona Espandi"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"oppure"</string> <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> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <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> diff --git a/packages/SystemUI/res/values-it/tiles_states_strings.xml b/packages/SystemUI/res/values-it/tiles_states_strings.xml index 2fd4f6d49aef..784a3091ef19 100644 --- a/packages/SystemUI/res/values-it/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-it/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"Disattivi"</item> <item msgid="3028994095749238254">"Attivi"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml index 6cf84e49f3af..9064d3dac5cd 100644 --- a/packages/SystemUI/res/values-iw/strings.xml +++ b/packages/SystemUI/res/values-iw/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"קלט"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"מכשירי שמיעה"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"ההפעלה מתבצעת…"</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"סיבוב אוטומטי"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"סיבוב אוטומטי של המסך"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"מיקום"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"לא ניתן לעדכן את ההגדרה הקבועה מראש"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"הגדרה קבועה מראש"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"כתוביות מיידיות"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"לבטל את חסימת המיקרופון של המכשיר?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"לבטל את חסימת המצלמה של המכשיר?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"לבטל את חסימת המצלמה והמיקרופון של המכשיר?"</string> @@ -698,8 +702,7 @@ <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> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"מצב תוכנת הצלצול"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"השתקה"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ביטול ההשתקה"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"רטט"</string> @@ -869,6 +872,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"מעבר לאפליקציה משמאל או למטה בזמן שימוש במסך מפוצל"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"מעבר לאפליקציה מימין או למעלה בזמן שימוש במסך מפוצל"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"כשהמסך מפוצל: החלפה בין אפליקציה אחת לאחרת"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"קלט"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"מעבר לשפה הבאה"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"מעבר לשפה הקודמת"</string> @@ -1411,15 +1416,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"התאמה אישית"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"סיום"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"סמל ההרחבה"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"או"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"נקודת האחיזה לגרירה"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"הגדרות המקלדת"</string> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <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> @@ -1437,7 +1456,7 @@ <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"מעבר למסך הבית"</string> <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"מחליקים כלפי מעלה עם שלוש אצבעות על לוח המגע"</string> <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"מעולה!"</string> - <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"השלמת את תנועת החזרה למסך הבית"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"סיימת לתרגל את תנועת החזרה למסך הבית"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"הצגת האפליקציות האחרונות"</string> <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"מחליקים למעלה ולוחצים לחיצה ארוכה עם שלוש אצבעות על לוח המגע"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"מעולה!"</string> diff --git a/packages/SystemUI/res/values-iw/tiles_states_strings.xml b/packages/SystemUI/res/values-iw/tiles_states_strings.xml index b5cb476484b2..e2ba375763c2 100644 --- a/packages/SystemUI/res/values-iw/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-iw/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"מצב מושבת"</item> <item msgid="3028994095749238254">"מצב פעיל"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml index 0793da9ef9a3..a23dac840796 100644 --- a/packages/SystemUI/res/values-ja/strings.xml +++ b/packages/SystemUI/res/values-ja/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"入力"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"補聴器"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"ON にしています…"</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"自動回転"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"画面を自動回転します"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"位置情報"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"プリセットを更新できませんでした"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"プリセット"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"自動字幕起こし"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"デバイスのマイクのブロックを解除しますか?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"デバイスのカメラのブロックを解除しますか?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"デバイスのカメラとマイクのブロックを解除しますか?"</string> @@ -589,8 +593,7 @@ <string name="media_projection_action_text" msgid="3634906766918186440">"今すぐ開始"</string> <string name="empty_shade_text" msgid="8935967157319717412">"通知はありません"</string> <string name="no_unseen_notif_text" msgid="395512586119868682">"新しい通知はありません"</string> - <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) --> - <skip /> + <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"通知のクールダウンを ON にしました"</string> <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"一度に多くの通知が届いた場合に、最長 2 分間自動的にデバイスの音量が小さくなりアラートも減ります。"</string> <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"OFF にする"</string> <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"ロック解除して以前の通知を表示"</string> @@ -698,8 +701,7 @@ <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> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"着信音のモード"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"ミュート"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ミュートを解除"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"バイブレーション"</string> @@ -809,7 +811,7 @@ <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"戻る"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> - <string name="keyboard_key_space" msgid="6980847564173394012">"スペース"</string> + <string name="keyboard_key_space" msgid="6980847564173394012">"Space"</string> <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string> <string name="keyboard_key_backspace" msgid="4095278312039628074">"Backspace"</string> <string name="keyboard_key_media_play_pause" msgid="8389984232732277478">"再生 / 一時停止"</string> @@ -869,6 +871,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"分割画面の使用時に右側または下部のアプリに切り替える"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"分割画面の使用時に左側または上部のアプリに切り替える"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"分割画面中: アプリを順に置換する"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"入力"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"次の言語に切り替える"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"前の言語に切り替える"</string> @@ -1411,15 +1415,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"カスタマイズ"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"完了"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"開くアイコン"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"または"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ドラッグ ハンドル"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"キーボードの設定"</string> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <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-ja/tiles_states_strings.xml b/packages/SystemUI/res/values-ja/tiles_states_strings.xml index 790445c93c14..683a4e889fbb 100644 --- a/packages/SystemUI/res/values-ja/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-ja/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"OFF"</item> <item msgid="3028994095749238254">"ON"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml index 8b513ffeba29..d0ff84296f3e 100644 --- a/packages/SystemUI/res/values-ka/strings.xml +++ b/packages/SystemUI/res/values-ka/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"შეყვანა"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"სმენის მოწყობილობები"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"ირთვება…"</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ავტოროტაცია"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"ეკრანის ავტომატური შეტრიალება"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"მდებარეობა"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"წინასწარ დაყენებული პარამეტრების განახლება ვერ მოხერხდა"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"წინასწარ დაყენებული"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"ავტოსუბტიტრები"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"გსურთ მოწყობილობის მიკროფონის განბლოკვა?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"გსურთ მოწყობილობის კამერის განბლოკვა?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"გსურთ მოწყობილობის კამერის და მიკროფონის განბლოკვა?"</string> @@ -589,8 +593,7 @@ <string name="media_projection_action_text" msgid="3634906766918186440">"დაწყება ახლავე"</string> <string name="empty_shade_text" msgid="8935967157319717412">"შეტყობინებები არ არის."</string> <string name="no_unseen_notif_text" msgid="395512586119868682">"ახალი შეტყობინებები არ არის"</string> - <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) --> - <skip /> + <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"შეტყობინების განტვირთვის პერიოდი ჩართულია"</string> <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"მოწყობილობის ხმა და გაფრთხილებები მცირდება 2 წუთის განმავლობაში, როდესაც ბევრ შეტყობინებას მიიღებთ ერთდროულად."</string> <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"გამორთვა"</string> <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"განბლოკეთ ძველი შეტყობინებების სანახავად"</string> @@ -698,8 +701,7 @@ <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> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"მრეკავის რეჟიმი"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"დადუმება"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"დადუმების მოხსნა"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"ვიბრაცია"</string> @@ -869,6 +871,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"ეკრანის გაყოფის გამოყენებისას აპზე მარჯვნივ ან ქვემოთ გადართვა"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"ეკრანის გაყოფის გამოყენებისას აპზე მარცხნივ ან ზემოთ გადართვა"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"ეკრანის გაყოფის დროს: ერთი აპის მეორით ჩანაცვლება"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"შეყვანა"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"შემდეგ ენაზე გადართვა"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"წინა ენაზე გადართვა"</string> @@ -1411,15 +1415,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"მორგება"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"მზადაა"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ხატულის გაფართოება"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ან"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"სახელური ჩავლებისთვის"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"კლავიატურის პარამეტრები"</string> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <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-ka/tiles_states_strings.xml b/packages/SystemUI/res/values-ka/tiles_states_strings.xml index 21f8102036c9..7c13eb53d5d8 100644 --- a/packages/SystemUI/res/values-ka/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-ka/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"გამორთულია"</item> <item msgid="3028994095749238254">"ჩართულია"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml index 4ff87a3a3c8d..94f8711cc7a0 100644 --- a/packages/SystemUI/res/values-kk/strings.xml +++ b/packages/SystemUI/res/values-kk/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Кіріс"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Есту аппараттары"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Қосылып жатыр…"</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Автоматты түрде бұру"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Автоматты айналатын экран"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Локация"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"Параметрлер жинағын жаңарту мүмкін болмады."</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Параметрлер жинағы"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Live Caption"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Құрылғы микрофонын блоктан шығару керек пе?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Құрылғы камерасын блоктан шығару керек пе?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Құрылғы камерасы мен микрофонын блоктан шығару керек пе?"</string> @@ -698,8 +702,7 @@ <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> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"қоңырау режимі"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"дыбысын өшіру"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"дыбысын қосу"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"дірілдету"</string> @@ -869,6 +872,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Бөлінген экранда оң не төмен жақтағы қолданбаға ауысу"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Бөлінген экранда сол не жоғары жақтағы қолданбаға ауысу"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"Экранды бөлу кезінде: бір қолданбаны басқасымен алмастыру"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Енгізу"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"Келесі тілге ауысу"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"Алдыңғы тілге ауысу"</string> @@ -1411,15 +1416,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Бейімдеу"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Дайын"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Жаю белгішесі"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"немесе"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Сүйрейтін тетік"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Пернетақта параметрлері"</string> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <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/tiles_states_strings.xml b/packages/SystemUI/res/values-kk/tiles_states_strings.xml index cf3aa69992f1..2b4c1ac355a2 100644 --- a/packages/SystemUI/res/values-kk/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-kk/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"Өшірулі"</item> <item msgid="3028994095749238254">"Қосулы"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml index 24524df2404f..2b178181aa3f 100644 --- a/packages/SystemUI/res/values-km/strings.xml +++ b/packages/SystemUI/res/values-km/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"បញ្ចូល"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"ឧបករណ៍ជំនួយការស្ដាប់"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"កំពុងបើក..."</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"បង្វិលស្វ័យប្រវត្តិ"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"បង្វិលអេក្រង់ស្វ័យប្រវត្តិ"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"ទីតាំង"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"មិនអាចប្ដូរការកំណត់ជាមុនបានទេ"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"កំណត់ជាមុន"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"អក្សររត់ក្នុងពេលជាក់ស្ដែង"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ឈប់ទប់ស្កាត់មីក្រូហ្វូនរបស់ឧបករណ៍ឬ?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"ឈប់ទប់ស្កាត់កាមេរ៉ារបស់ឧបករណ៍ឬ?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"ឈប់ទប់ស្កាត់កាមេរ៉ា និងមីក្រូហ្វូនរបស់ឧបករណ៍ឬ?"</string> @@ -589,8 +593,7 @@ <string name="media_projection_action_text" msgid="3634906766918186440">"ចាប់ផ្ដើមឥឡូវ"</string> <string name="empty_shade_text" msgid="8935967157319717412">"គ្មានការជូនដំណឹង"</string> <string name="no_unseen_notif_text" msgid="395512586119868682">"គ្មានការជូនដំណឹងថ្មីៗទេ"</string> - <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) --> - <skip /> + <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"ឥឡូវនេះ ការបន្ថយសំឡេងការជូនដំណឹងត្រូវបានបើក"</string> <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"កម្រិតសំឡេង និងការជូនដំណឹងនៅលើឧបករណ៍របស់អ្នកត្រូវបានកាត់បន្ថយដោយស្វ័យប្រវត្តិរហូតដល់ 2 នាទី នៅពេលអ្នកទទួលបានការជូនដំណឹងច្រើនពេកក្នុងពេលតែមួយ។"</string> <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"បិទ"</string> <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"ដោះសោដើម្បីមើលការជូនដំណឹងចាស់ៗ"</string> @@ -698,8 +701,7 @@ <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> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"មុខងារកម្មវិធីរោទ៍"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"បិទសំឡេង"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"បើកសំឡេង"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"ញ័រ"</string> @@ -869,6 +871,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"ប្ដូរទៅកម្មវិធីនៅខាងស្ដាំ ឬខាងក្រោម ពេលកំពុងប្រើមុខងារបំបែកអេក្រង់"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"ប្ដូរទៅកម្មវិធីនៅខាងឆ្វេង ឬខាងលើ ពេលកំពុងប្រើមុខងារបំបែកអេក្រង់"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"ក្នុងអំឡុងពេលប្រើមុខងារបំបែកអេក្រង់៖ ជំនួសកម្មវិធីពីមួយទៅមួយទៀត"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"បញ្ចូល"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"ប្ដូរទៅភាសាបន្ទាប់"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"ប្ដូរទៅភាសាមុន"</string> @@ -1411,15 +1415,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"ប្ដូរតាមបំណង"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"រួចរាល់"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"រូបតំណាង \"ពង្រីក\""</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ឬ"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ដងអូស"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"ការកំណត់ក្ដារចុច"</string> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <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/tiles_states_strings.xml b/packages/SystemUI/res/values-km/tiles_states_strings.xml index 54790f6a028e..3c15fd3a35e4 100644 --- a/packages/SystemUI/res/values-km/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-km/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"បិទ"</item> <item msgid="3028994095749238254">"បើក"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml index bfaa43919aa3..b74a0535bdd4 100644 --- a/packages/SystemUI/res/values-kn/strings.xml +++ b/packages/SystemUI/res/values-kn/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"ಇನ್ಪುಟ್"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"ಶ್ರವಣ ಸಾಧನಗಳು"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"ಆನ್ ಮಾಡಲಾಗುತ್ತಿದೆ..."</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ಸ್ವಯಂ-ತಿರುಗುವಿಕೆ"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"ಪರದೆಯನ್ನು ಸ್ವಯಂ-ತಿರುಗಿಸಿ"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"ಸ್ಥಳ"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"ಪ್ರಿಸೆಟ್ ಅನ್ನು ಅಪ್ಡೇಟ್ ಮಾಡಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"ಪ್ರಿಸೆಟ್"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"ಲೈವ್ ಕ್ಯಾಪ್ಶನ್"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ಸಾಧನದ ಮೈಕ್ರೋಫೋನ್ ನಿರ್ಬಂಧವನ್ನು ತೆಗೆಯಬೇಕೆ?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"ಸಾಧನದ ಕ್ಯಾಮರಾ ನಿರ್ಬಂಧವನ್ನು ತೆಗೆಯಬೇಕೆ?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"ಸಾಧನದ ಕ್ಯಾಮರಾ ಮತ್ತು ಮೈಕ್ರೋಫೋನ್ ಅನ್ನು ಅನ್ಬ್ಲಾಕ್ ಮಾಡಬೇಕೇ?"</string> @@ -589,8 +593,7 @@ <string name="media_projection_action_text" msgid="3634906766918186440">"ಈಗ ಪ್ರಾರಂಭಿಸಿ"</string> <string name="empty_shade_text" msgid="8935967157319717412">"ಯಾವುದೇ ಅಧಿಸೂಚನೆಗಳಿಲ್ಲ"</string> <string name="no_unseen_notif_text" msgid="395512586119868682">"ಯಾವುದೇ ಹೊಸ ಅಧಿಸೂಚನೆಗಳಿಲ್ಲ"</string> - <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) --> - <skip /> + <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"ನೋಟಿಫಿಕೇಶನ್ ಕೂಲ್ಡೌನ್ ಈಗ ಆನ್ ಆಗಿದೆ"</string> <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"ನೀವು ಏಕಕಾಲದಲ್ಲಿ ತೀರಾ ಹೆಚ್ಚು ನೋಟಿಫಿಕೇಶನ್ಗಳನ್ನು ಪಡೆದಾಗ 2 ನಿಮಿಷಗಳವರೆಗೆ ನಿಮ್ಮ ಸಾಧನದ ವಾಲ್ಯೂಮ್ ಮತ್ತು ಅಲರ್ಟ್ಗಳನ್ನು ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಕಡಿಮೆ ಮಾಡಲಾಗುತ್ತದೆ."</string> <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"ಆಫ್ ಮಾಡಿ"</string> <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"ಹಳೆಯ ಅಧಿಸೂಚನೆಗಳನ್ನು ನೋಡಲು ಅನ್ಲಾಕ್ ಮಾಡಿ"</string> @@ -698,8 +701,7 @@ <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> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"ರಿಂಗರ್ ಮೋಡ್"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"ಮ್ಯೂಟ್ ಮಾಡಿ"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ಅನ್ಮ್ಯೂಟ್ ಮಾಡಿ"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"ವೈಬ್ರೇಟ್"</string> @@ -869,6 +871,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"ಪರದೆ ಬೇರ್ಪಡಿಸಿ ಮೋಡ್ ಬಳಸುವಾಗ ಬಲಭಾಗ ಅಥವಾ ಕೆಳಭಾಗದಲ್ಲಿರುವ ಆ್ಯಪ್ಗೆ ಬದಲಿಸಿ"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"ಪರದೆ ಬೇರ್ಪಡಿಸಿ ಮೋಡ್ ಬಳಸುವಾಗ ಎಡಭಾಗ ಅಥವಾ ಮೇಲ್ಭಾಗದಲ್ಲಿರುವ ಆ್ಯಪ್ಗೆ ಬದಲಿಸಿ"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"ಸ್ಕ್ರೀನ್ ಬೇರ್ಪಡಿಸುವ ಸಮಯದಲ್ಲಿ: ಒಂದು ಆ್ಯಪ್ನಿಂದ ಮತ್ತೊಂದು ಆ್ಯಪ್ಗೆ ಬದಲಿಸಿ"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"ಇನ್ಪುಟ್"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"ಮುಂದಿನ ಭಾಷೆಗೆ ಬದಲಿಸಿ"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"ಹಿಂದಿನ ಭಾಷೆಗೆ ಬದಲಿಸಿ"</string> @@ -1411,15 +1415,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"ಕಸ್ಟಮೈಸ್ ಮಾಡಿ"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"ಮುಗಿದಿದೆ"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ವಿಸ್ತೃತಗೊಳಿಸುವ ಐಕಾನ್"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ಅಥವಾ"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ಡ್ರ್ಯಾಗ್ ಹ್ಯಾಂಡಲ್"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"ಕೀಬೋರ್ಡ್ ಸೆಟ್ಟಿಂಗ್ಗಳು"</string> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <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> @@ -1437,7 +1455,7 @@ <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"ಮುಖಪುಟಕ್ಕೆ ಹೋಗಿ"</string> <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"ಟಚ್ಪ್ಯಾಡ್ನಲ್ಲಿ ಮೂರು ಬೆರಳಿಂದ ಮೇಲಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ"</string> <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"ಭೇಷ್!"</string> - <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"ನೀವು ಗೋ ಹೋಮ್ ಗೆಸ್ಚರ್ ಅನ್ನು ಪೂರ್ಣಗೊಳಿಸಿದ್ದೀರಿ"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"ನೀವು ಗೋ ಹೋಮ್ ಜೆಸ್ಚರ್ ಅನ್ನು ಪೂರ್ಣಗೊಳಿಸಿದ್ದೀರಿ"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"ಇತ್ತೀಚಿನ ಆ್ಯಪ್ಗಳನ್ನು ವೀಕ್ಷಿಸಿ"</string> <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"ನಿಮ್ಮ ಟಚ್ಪ್ಯಾಡ್ನಲ್ಲಿ ಮೂರು ಬೆರಳುಗಳನ್ನು ಬಳಸಿ ಮೇಲಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ ಮತ್ತು ಹೋಲ್ಡ್ ಮಾಡಿ"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"ಭೇಷ್!"</string> diff --git a/packages/SystemUI/res/values-kn/tiles_states_strings.xml b/packages/SystemUI/res/values-kn/tiles_states_strings.xml index c7cb2b16a2f3..5a188f19a5ac 100644 --- a/packages/SystemUI/res/values-kn/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-kn/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"ಆಫ್"</item> <item msgid="3028994095749238254">"ಆನ್"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml index fee96f981152..cc2cb909058a 100644 --- a/packages/SystemUI/res/values-ko/strings.xml +++ b/packages/SystemUI/res/values-ko/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"입력"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"보청기"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"켜는 중..."</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"자동 회전"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"화면 자동 회전"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"위치"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"사전 설정을 업데이트할 수 없음"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"미리 설정"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"실시간 자막"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"기기 마이크를 차단 해제하시겠습니까?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"기기 카메라를 차단 해제하시겠습니까?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"기기 카메라 및 마이크를 차단 해제하시겠습니까?"</string> @@ -698,8 +702,7 @@ <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> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"벨소리 장치 모드"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"음소거"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"음소거 해제"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"진동"</string> @@ -869,6 +872,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"화면 분할을 사용하는 중에 오른쪽 또는 아래쪽에 있는 앱으로 전환"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"화면 분할을 사용하는 중에 왼쪽 또는 위쪽에 있는 앱으로 전환하기"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"화면 분할 중: 다른 앱으로 바꾸기"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"입력"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"다음 언어로 전환"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"이전 언어로 전환"</string> @@ -1411,15 +1416,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"맞춤설정"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"완료"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"확장 아이콘"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"또는"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"드래그 핸들"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"키보드 설정"</string> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <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> @@ -1436,7 +1455,7 @@ <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"돌아가기 동작을 완료했습니다."</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"홈으로 이동"</string> <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"세 손가락을 사용해 터치패드에서 위로 스와이프하세요."</string> - <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"아주 좋습니다"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"잘하셨습니다"</string> <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"홈으로 이동 동작을 완료했습니다."</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"최근 앱 보기"</string> <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"세 손가락을 사용해 터치패드에서 위로 스와이프한 후 잠시 기다리세요."</string> diff --git a/packages/SystemUI/res/values-ko/tiles_states_strings.xml b/packages/SystemUI/res/values-ko/tiles_states_strings.xml index bc4740dbc23a..bfa11271bbab 100644 --- a/packages/SystemUI/res/values-ko/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-ko/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"사용 안함"</item> <item msgid="3028994095749238254">"사용"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml index 3c58eb7bd559..bcf594db3ded 100644 --- a/packages/SystemUI/res/values-ky/strings.xml +++ b/packages/SystemUI/res/values-ky/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Киргизүү"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Угуу аппараттары"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Күйгүзүлүүдө…"</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Авто буруу"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Экранды авто буруу"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Жайгашкан жер"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"Алдын ала коюлган параметрлер жаңыртылган жок"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Алдын ала коюлган параметрлер"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Ыкчам коштомо жазуулар"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Түзмөктүн микрофонун бөгөттөн чыгарасызбы?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Түзмөктүн камерасын бөгөттөн чыгарасызбы?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Түзмөктүн камерасы менен микрофону бөгөттөн чыгарылсынбы?"</string> @@ -698,8 +702,7 @@ <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> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"коңгуроо режими"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"үнсүз"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"үнүн чыгаруу"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"дирилдөө"</string> @@ -869,6 +872,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Бөлүнгөн экранда сол же төмөн жактагы колдонмого которулуу"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Бөлүнгөн экранды колдонуп жатканда сол же жогору жактагы колдонмого которулуңуз"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"Экранды бөлүү режиминде бир колдонмону экинчисине алмаштыруу"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Киргизүү"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"Кийинки тилге которулуу"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"Мурунку тилге которулуу"</string> @@ -1411,15 +1416,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Ыңгайлаштыруу"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Бүттү"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Жайып көрсөтүү сүрөтчөсү"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"же"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Cүйрөө маркери"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Баскычтоп параметрлери"</string> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <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/tiles_states_strings.xml b/packages/SystemUI/res/values-ky/tiles_states_strings.xml index 694967e3d8d6..e9d9612a4e47 100644 --- a/packages/SystemUI/res/values-ky/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-ky/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"Өчүк"</item> <item msgid="3028994095749238254">"Күйүк"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml index 1dd534df9be0..4898fa8d823a 100644 --- a/packages/SystemUI/res/values-lo/strings.xml +++ b/packages/SystemUI/res/values-lo/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"ການປ້ອນຂໍ້ມູນ"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"ເຄື່ອງຊ່ວຍຟັງ"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"ກຳລັງເປີດ..."</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ໝຸນອັດຕະໂນມັດ"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"ໝຸນໜ້າຈໍອັດຕະໂນມັດ"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"ສະຖານທີ່"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"ບໍ່ສາມາດອັບເດດການຕັ້ງຄ່າລ່ວງໜ້າໄດ້"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"ຄ່າທີ່ກຳນົດລ່ວງໜ້າ"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"ຄຳບັນຍາຍສົດ"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ປົດບລັອກໄມໂຄຣໂຟນອຸປະກອນບໍ?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"ປົດບລັອກກ້ອງຖ່າຍຮູບອຸປະກອນບໍ?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"ຍົກເລີກການບລັອກກ້ອງຖ່າຍຮູບ ຫຼື ໄມໂຄຣໂຟນອຸປະກອນບໍ?"</string> @@ -589,8 +593,7 @@ <string name="media_projection_action_text" msgid="3634906766918186440">"ເລີ່ມດຽວນີ້"</string> <string name="empty_shade_text" msgid="8935967157319717412">"ບໍ່ມີການແຈ້ງເຕືອນ"</string> <string name="no_unseen_notif_text" msgid="395512586119868682">"ບໍ່ມີການແຈ້ງເຕືອນໃໝ່"</string> - <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) --> - <skip /> + <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"ຕອນນີ້ຄູດາວການແຈ້ງເຕືອນເປີດຢູ່"</string> <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"ສຽງ ແລະ ແຈ້ງເຕືອນອຸປະກອນຂອງທ່ານຖືກຫຼຸດລົງໂດຍອັດຕະໂນມັດເປັນເວລາເຖິງ 2 ນາທີເມື່ອທ່ານໄດ້ຮັບການແຈ້ງເຕືອນຫຼາຍເກີນໄປໃນຄັ້ງດຽວ."</string> <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"ປິດ"</string> <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"ປົດລັອກເພື່ອເບິ່ງການແຈ້ງເຕືອນເກົ່າ"</string> @@ -868,6 +871,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"ສະຫຼັບໄປໃຊ້ແອັບຢູ່ຂວາ ຫຼື ທາງລຸ່ມໃນຂະນະທີ່ໃຊ້ແບ່ງໜ້າຈໍ"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"ສະຫຼັບໄປໃຊ້ແອັບຢູ່ຊ້າຍ ຫຼື ທາງເທິງໃນຂະນະທີ່ໃຊ້ແບ່ງໜ້າຈໍ"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"ໃນລະຫວ່າງແບ່ງໜ້າຈໍ: ໃຫ້ປ່ຽນຈາກແອັບໜຶ່ງເປັນອີກແອັບໜຶ່ງ"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"ການປ້ອນຂໍ້ມູນ"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"ສະຫຼັບເປັນພາສາຖັດໄປ"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"ສະຫຼັບເປັນພາສາກ່ອນໜ້າ"</string> @@ -1410,15 +1415,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"ປັບແຕ່ງ"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"ແລ້ວໆ"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ໄອຄອນຂະຫຍາຍ"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ຫຼື"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ບ່ອນຈັບລາກ"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"ການຕັ້ງຄ່າແປ້ນພິມ"</string> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <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/tiles_states_strings.xml b/packages/SystemUI/res/values-lo/tiles_states_strings.xml index 9386e00af195..34af9aae31e3 100644 --- a/packages/SystemUI/res/values-lo/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-lo/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"ປິດ"</item> <item msgid="3028994095749238254">"ເປີດ"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml index a2d26c76d33a..aff0d3085bfa 100644 --- a/packages/SystemUI/res/values-lt/strings.xml +++ b/packages/SystemUI/res/values-lt/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Įvestis"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Klausos aparatai"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Įjungiama…"</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automatinis pasukimas"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Automatiškai sukti ekraną"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Vietovė"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"Išankstinių nustatymų atnaujinti nepavyko"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Išankstiniai nustatymai"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Subtitrai realiuoju laiku"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Panaikinti įrenginio mikrofono blokavimą?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Panaikinti įrenginio fotoaparato blokavimą?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Panaikinti įrenginio fotoaparato ir mikrofono blokavimą?"</string> @@ -589,8 +593,7 @@ <string name="media_projection_action_text" msgid="3634906766918186440">"Pradėti dabar"</string> <string name="empty_shade_text" msgid="8935967157319717412">"Nėra įspėjimų"</string> <string name="no_unseen_notif_text" msgid="395512586119868682">"Naujų pranešimų nėra"</string> - <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) --> - <skip /> + <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"Pranešimų neaktyvumo laikotarpis dabar įjungtas"</string> <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Jūsų įrenginio garsumas ir įspėjimai automatiškai sumažinami iki dviejų minučių, kai iš karto gaunate per daug pranešimų."</string> <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Išjungti"</string> <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Atrakinę matykite senesnius pranešimus"</string> @@ -698,8 +701,7 @@ <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Fiksuotas"</string> <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Galvos stebėjimas"</string> <string name="volume_ringer_change" msgid="3574969197796055532">"Palieskite, kad pakeistumėte skambučio režimą"</string> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"skambučio režimas"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"nutildyti"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"įjungti garsą"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibruoti"</string> @@ -869,6 +871,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Perjunkite į programą dešinėje arba apačioje išskaidyto ekrano režimu"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Perjunkite į programą kairėje arba viršuje išskaidyto ekrano režimu"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"Išskaidyto ekrano režimu: pakeisti iš vienos programos į kitą"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Įvestis"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"Perjungti į kitą kalbą"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"Perjungti į ankstesnę kalbą"</string> @@ -1411,15 +1415,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Tinkinti"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Atlikta"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Išskleidimo piktograma"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"arba"</string> <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> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <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-lt/tiles_states_strings.xml b/packages/SystemUI/res/values-lt/tiles_states_strings.xml index c975e7e3cc80..124f49c7d22e 100644 --- a/packages/SystemUI/res/values-lt/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-lt/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"Išjungta"</item> <item msgid="3028994095749238254">"Įjungta"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml index 98eebd3e5834..b0b417675c1c 100644 --- a/packages/SystemUI/res/values-lv/strings.xml +++ b/packages/SystemUI/res/values-lv/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Ievade"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Dzirdes aparāti"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Notiek ieslēgšana…"</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automātiska pagriešana"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Automātiska ekrāna pagriešana"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Atrašanās vieta"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"Nevarēja atjaunināt pirmsiestatījumu"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Pirmsiestatījums"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Subtitri reāllaikā"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Vai atbloķēt ierīces mikrofonu?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Vai vēlaties atbloķēt ierīces kameru?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Vai atbloķēt ierīces kameru un mikrofonu?"</string> @@ -698,8 +702,7 @@ <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Fiksēts"</string> <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Seko galvai"</string> <string name="volume_ringer_change" msgid="3574969197796055532">"Pieskarieties, lai mainītu zvanītāja režīmu."</string> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"zvanītāja režīms"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"izslēgt skaņu"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ieslēgt skaņu"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrēt"</string> @@ -869,6 +872,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Pāriet uz lietotni pa labi/lejā, kamēr izmantojat sadalīto ekrānu."</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Pāriet uz lietotni pa kreisi/augšā, kamēr izmantojat sadalīto ekrānu."</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"Ekrāna sadalīšanas režīmā: pārvietot lietotni no viena ekrāna uz otru"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Ievade"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"Pārslēgt uz nākamo valodu"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"Pārslēgt uz iepriekšējo valodu"</string> @@ -1411,15 +1416,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Pielāgot"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Gatavs"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Izvēršanas ikona"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"vai"</string> <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> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <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-lv/tiles_states_strings.xml b/packages/SystemUI/res/values-lv/tiles_states_strings.xml index c65a1d429492..e5cb1758b783 100644 --- a/packages/SystemUI/res/values-lv/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-lv/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"Izslēgts"</item> <item msgid="3028994095749238254">"Ieslēgts"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml index f136441a7338..e30cf21fceec 100644 --- a/packages/SystemUI/res/values-mk/strings.xml +++ b/packages/SystemUI/res/values-mk/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Влез"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Слушни помагала"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Се вклучува…"</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Автоматско ротирање"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Автоматско ротирање на екранот"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Локација"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"Не можеше да се ажурира зададената вредност"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Зададени вредности"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Автоматски титлови"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Да се одблокира пристапот до микрофонот на уредот?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Да се одблокира пристапот до камерата на уредот?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Да се одблокира пристапот до камерата и микрофонот на уредот?"</string> @@ -698,8 +702,7 @@ <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> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"режим на ѕвонче"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"исклучен звук"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"вклучен звук"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"вибрации"</string> @@ -869,6 +872,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Префрлете се на апликацијата десно или долу при користењето поделен екран"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Префрлете се на апликацијата лево или горе при користењето поделен екран"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"При поделен екран: префрлете ги аплик. од едната на другата страна"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Внесување"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"Префрлете на следниот јазик"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"Префрлете на претходниот јазик"</string> @@ -1411,15 +1416,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Приспособете"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Готово"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Икона за проширување"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"или"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Рачка за влечење"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Поставки за тастатурата"</string> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <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> @@ -1428,7 +1447,7 @@ <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> + <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Прикажи ги неодамнешните апликации"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Готово"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Назад"</string> <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Повлечете налево или надесно со три прста на допирната подлога"</string> @@ -1438,7 +1457,7 @@ <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Повлечете нагоре со три прсти на допирната подлога"</string> <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Одлично!"</string> <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Го завршивте движењето за враќање на почетниот екран"</string> - <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Прегледајте ги неодамнешните апликации"</string> + <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Прикажи ги неодамнешните апликации"</string> <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Повлечете нагоре и задржете со три прста на допирната подлога"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Одлично!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Го завршивте движењето за прегледување на неодамнешните апликации."</string> diff --git a/packages/SystemUI/res/values-mk/tiles_states_strings.xml b/packages/SystemUI/res/values-mk/tiles_states_strings.xml index a8d96950b6f8..61539d62baf7 100644 --- a/packages/SystemUI/res/values-mk/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-mk/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"Исклучено"</item> <item msgid="3028994095749238254">"Вклучено"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml index 3378b1143ca9..42f67c6558c2 100644 --- a/packages/SystemUI/res/values-ml/strings.xml +++ b/packages/SystemUI/res/values-ml/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"ഇൻപുട്ട്"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"ശ്രവണ സഹായികൾ"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"ഓണാക്കുന്നു…"</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"സ്ക്രീൻ സ്വയമേവ തിരിയൽ"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"സ്ക്രീൻ സ്വയമേവ തിരിക്കുക"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"ലൊക്കേഷൻ"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"പ്രീസെറ്റ് അപ്ഡേറ്റ് ചെയ്യാനായില്ല"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"പ്രീസെറ്റ്"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"തത്സമയ ക്യാപ്ഷൻ"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ഉപകരണ മൈക്രോഫോൺ അൺബ്ലോക്ക് ചെയ്യണോ?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"ഉപകരണ ക്യാമറ അൺബ്ലോക്ക് ചെയ്യണോ?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"ഉപകരണ ക്യാമറയോ മൈക്രോഫോണോ അൺബ്ലോക്ക് ചെയ്യണോ?"</string> @@ -698,8 +702,7 @@ <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> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"റിംഗർ മോഡ്"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"മ്യൂട്ട് ചെയ്യുക"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"അൺമ്യൂട്ട് ചെയ്യുക"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"വൈബ്രേറ്റ് ചെയ്യുക"</string> @@ -869,6 +872,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"സ്ക്രീൻ വിഭജന മോഡ് ഉപയോഗിക്കുമ്പോൾ വലതുവശത്തെ/താഴത്തെ ആപ്പിലേക്ക് മാറുക"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"സ്ക്രീൻ വിഭജന മോഡ് ഉപയോഗിക്കുമ്പോൾ ഇടതുവശത്തെ/മുകളിലെ ആപ്പിലേക്ക് മാറൂ"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"സ്ക്രീൻ വിഭജന മോഡിൽ: ഒരു ആപ്പിൽ നിന്ന് മറ്റൊന്നിലേക്ക് മാറുക"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"ഇൻപുട്ട്"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"അടുത്ത ഭാഷയിലേക്ക് മാറുക"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"മുമ്പത്തെ ഭാഷയിലേക്ക് മാറുക"</string> @@ -1411,15 +1416,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"ഇഷ്ടാനുസൃതമാക്കുക"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"പൂർത്തിയായി"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"വികസിപ്പിക്കൽ ഐക്കൺ"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"അല്ലെങ്കിൽ"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"വലിച്ചിടുന്നതിനുള്ള ഹാൻഡിൽ"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"കീബോർഡ് ക്രമീകരണം"</string> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <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/tiles_states_strings.xml b/packages/SystemUI/res/values-ml/tiles_states_strings.xml index 609fdde3f5de..c1278d46440f 100644 --- a/packages/SystemUI/res/values-ml/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-ml/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"ഓഫാണ്"</item> <item msgid="3028994095749238254">"ഓണാണ്"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml index 9aca3ee05287..c87f98acd3e9 100644 --- a/packages/SystemUI/res/values-mn/strings.xml +++ b/packages/SystemUI/res/values-mn/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Оролт"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Сонсголын төхөөрөмж"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Асааж байна…"</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Автоматаар эргэх"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Дэлгэцийг автоматаар эргүүлэх"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Байршил"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"Урьдчилсан тохируулгыг шинэчилж чадсангүй"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Урьдчилсан тохируулга"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Шууд тайлбар"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Төхөөрөмжийн микрофоныг блокоос гаргах уу?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Төхөөрөмжийн камерыг блокоос гаргах уу?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Төхөөрөмжийн камер болон микрофоныг блокоос гаргах уу?"</string> @@ -698,8 +702,7 @@ <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> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"хонхны горим"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"дууг хаах"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"дууг нээх"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"чичрэх"</string> @@ -869,6 +872,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Дэлгэц хуваахыг ашиглаж байхдаа баруун талд эсвэл доор байх апп руу сэлгэ"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Дэлгэц хуваахыг ашиглаж байхдаа зүүн талд эсвэл дээр байх апп руу сэлгэ"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"Дэлгэц хуваах үеэр: аппыг нэгээс нөгөөгөөр солих"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Оролт"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"Дараагийн хэл рүү сэлгэх"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"Өмнөх хэл рүү сэлгэх"</string> @@ -1411,15 +1416,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Өөрчлөх"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Болсон"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Дэлгэх дүрс тэмдэг"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"эсвэл"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Чирэх бариул"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Гарын тохиргоо"</string> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <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> @@ -1443,7 +1462,7 @@ <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Сайн байна!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Та саяхны аппуудыг харах зангааг гүйцэтгэсэн."</string> <string name="tutorial_action_key_title" msgid="8172535792469008169">"Бүх аппыг харах"</string> - <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Гар дээр тань байх тусгай товчийг дарна уу"</string> + <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Гар дээрх тусгай товчлуурыг дарна уу"</string> <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"Сайн байна!"</string> <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"Та бүх аппыг харах зангааг гүйцэтгэлээ"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Гарын арын гэрэл"</string> diff --git a/packages/SystemUI/res/values-mn/tiles_states_strings.xml b/packages/SystemUI/res/values-mn/tiles_states_strings.xml index a3f54541880d..da890cc5a157 100644 --- a/packages/SystemUI/res/values-mn/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-mn/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"Унтраалттай"</item> <item msgid="3028994095749238254">"Асаалттай"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml index 82a001a37420..204399c77b96 100644 --- a/packages/SystemUI/res/values-mr/strings.xml +++ b/packages/SystemUI/res/values-mr/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"इनपुट"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"श्रवणयंत्रे"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"सुरू करत आहे…"</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ऑटो-रोटेट"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"ऑटो-रोटेट स्क्रीन"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"स्थान"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"प्रीसेट अपडेट करता आले नाही"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"प्रीसेट"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"लाइव्ह कॅप्शन"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"डिव्हाइसचा मायक्रोफोन अनब्लॉक करायचा आहे का?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"डिव्हाइसचा कॅमेरा अनब्लॉक करायचा आहे का?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"डिव्हाइसचा कॅमेरा आणि मायक्रोफोन अनब्लॉक करायचा आहे का?"</string> @@ -589,8 +593,7 @@ <string name="media_projection_action_text" msgid="3634906766918186440">"आता सुरू करा"</string> <string name="empty_shade_text" msgid="8935967157319717412">"सूचना नाहीत"</string> <string name="no_unseen_notif_text" msgid="395512586119868682">"नवीन सूचना नाहीत"</string> - <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) --> - <skip /> + <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"आता नोटिफिकेशन कूलडाउन सुरू आहे"</string> <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"एकाच वेळी अनेक नोटिफिकेशन मिळाल्यास, डिव्हाइसचा आवाज आणि सूचना आपोआप कमाल २ मिनिटांपर्यंत कमी होतात."</string> <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"बंद करा"</string> <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"जुन्या सूचना पाहण्यासाठी अनलॉक करा"</string> @@ -698,8 +701,7 @@ <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> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"रिंगर मोड"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"म्यूट करा"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"म्यूट काढून टाका"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"व्हायब्रेट करा"</string> @@ -869,6 +871,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"स्प्लिट स्क्रीन वापरताना उजवीकडील किंवा खालील अॅपवर स्विच करा"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"स्प्लिट स्क्रीन वापरताना डावीकडील किंवा वरील अॅपवर स्विच करा"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"स्प्लिट स्क्रीनदरम्यान: एक अॅप दुसऱ्या अॅपने बदला"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"इनपुट"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"पुढील भाषेवर स्विच करा"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"मागील भाषेवर स्विच करा"</string> @@ -1411,15 +1415,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"कस्टमाइझ करा"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"पूर्ण झाले"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"विस्तार करा आयकन"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"किंवा"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ड्रॅग हॅंडल"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"कीबोर्ड सेटिंग्ज"</string> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <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/tiles_states_strings.xml b/packages/SystemUI/res/values-mr/tiles_states_strings.xml index 54c320c953d5..3ea25a68922b 100644 --- a/packages/SystemUI/res/values-mr/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-mr/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"बंद आहे"</item> <item msgid="3028994095749238254">"सुरू आहे"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml index bc01916bb3e5..ce2b3f07c547 100644 --- a/packages/SystemUI/res/values-ms/strings.xml +++ b/packages/SystemUI/res/values-ms/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Input"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Alat bantu pendengaran"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Menghidupkan…"</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Autoputar"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Autoputar skrin"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Lokasi"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"Tidak dapat mengemaskinikan pratetapan"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Pratetapan"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Sari Kata Langsung"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Nyahsekat mikrofon peranti?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Nyahsekat kamera peranti?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Nyahsekat kamera dan mikrofon peranti?"</string> @@ -589,8 +593,7 @@ <string name="media_projection_action_text" msgid="3634906766918186440">"Mulakan sekarang"</string> <string name="empty_shade_text" msgid="8935967157319717412">"Tiada pemberitahuan"</string> <string name="no_unseen_notif_text" msgid="395512586119868682">"Tiada pemberitahuan baharu"</string> - <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) --> - <skip /> + <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"Tempoh bertenang pemberitahuan dihidupkan"</string> <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Kelantangan, makluman peranti dikurangkan secara automatik hingga 2 minit apabila menerima banyak pemberitahuan serentak."</string> <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Matikan"</string> <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Buka kunci untuk melihat pemberitahuan lama"</string> @@ -698,8 +701,7 @@ <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Tetap"</string> <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Penjejakan Kepala"</string> <string name="volume_ringer_change" msgid="3574969197796055532">"Ketik untuk menukar mod pendering"</string> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"mod pendering"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"redam"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"nyahredam"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"getar"</string> @@ -869,6 +871,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Tukar kepada apl di sebelah kanan/bawah semasa menggunakan skrin pisah"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Tukar kepada apl di sebelah kiri/atas semasa menggunakan skrin pisah"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"Semasa skrin pisah: gantikan apl daripada satu apl kepada apl lain"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Input"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"Beralih kepada bahasa seterusnya"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"Beralih kepada bahasa sebelumnya"</string> @@ -1411,15 +1415,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Sesuaikan"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Selesai"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Kembangkan ikon"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"atau"</string> <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> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <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> diff --git a/packages/SystemUI/res/values-ms/tiles_states_strings.xml b/packages/SystemUI/res/values-ms/tiles_states_strings.xml index 174e416e3508..51f215b31931 100644 --- a/packages/SystemUI/res/values-ms/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-ms/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"Mati"</item> <item msgid="3028994095749238254">"Hidup"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml index 55626f065caa..a3568f2b56bb 100644 --- a/packages/SystemUI/res/values-my/strings.xml +++ b/packages/SystemUI/res/values-my/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"အဝင်"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"နားကြားကိရိယာ"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"ဖွင့်နေသည်…"</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"အော်တို-လည်"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"မျက်နှာပြင်အား အလိုအလျောက်လှည့်ခြင်း"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"တည်နေရာ"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"အသင့်သုံးကို အပ်ဒိတ်လုပ်၍မရပါ"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"ကြိုတင်သတ်မှတ်ချက်"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"တိုက်ရိုက်စာတန်း"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"စက်၏မိုက်ခရိုဖုန်းကို ပြန်ဖွင့်မလား။"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"စက်၏ကင်မရာကို ပြန်ဖွင့်မလား။"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"စက်၏ကင်မရာနှင့် မိုက်ခရိုဖုန်းကို ပြန်ဖွင့်မလား။"</string> @@ -698,8 +702,7 @@ <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> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"အသံမြည်မုဒ်"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"အသံပိတ်ရန်"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"အသံဖွင့်ရန်"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"တုန်ခါမှု"</string> @@ -869,6 +872,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"မျက်နှာပြင်ခွဲ၍ပြသခြင်း သုံးစဉ် ညာ (သို့) အောက်ရှိအက်ပ်သို့ ပြောင်းရန်"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"မျက်နှာပြင် ခွဲ၍ပြသခြင်းသုံးစဉ် ဘယ် (သို့) အထက်ရှိအက်ပ်သို့ ပြောင်းရန်"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"မျက်နှာပြင် ခွဲ၍ပြသစဉ်- အက်ပ်တစ်ခုကို နောက်တစ်ခုနှင့် အစားထိုးရန်"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"စာရိုက်ခြင်း"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"နောက်ဘာသာစကားသို့ ပြောင်းရန်"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"ယခင်ဘာသာစကားသို့ ပြောင်းရန်"</string> @@ -1411,15 +1416,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"စိတ်ကြိုက်လုပ်ရန်"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"ပြီးပြီ"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ပိုပြရန် သင်္ကေတ"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"သို့မဟုတ်"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ဖိဆွဲအထိန်း"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"ကီးဘုတ်ဆက်တင်များ"</string> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <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-my/tiles_states_strings.xml b/packages/SystemUI/res/values-my/tiles_states_strings.xml index f665a00a5214..7af75166de11 100644 --- a/packages/SystemUI/res/values-my/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-my/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"ပိတ်"</item> <item msgid="3028994095749238254">"ဖွင့်"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml index 942018514862..38b267332f47 100644 --- a/packages/SystemUI/res/values-nb/strings.xml +++ b/packages/SystemUI/res/values-nb/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Innenhet"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Høreapparater"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Slår på …"</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Rotér automatisk"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Rotér skjermen automatisk"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Sted"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"Kunne ikke oppdatere forhåndsinnstillingen"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Forhåndsinnstilling"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Direkteteksting"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Vil du oppheve blokkeringen av enhetsmikrofonen?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Vil du oppheve blokkeringen av enhetskameraet?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Vil du oppheve blokkeringen av enhetskameraet og -mikrofonen?"</string> @@ -698,8 +702,7 @@ <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Fast"</string> <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Hodesporing"</string> <string name="volume_ringer_change" msgid="3574969197796055532">"Trykk for å endre ringemodus"</string> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"modus for ringeprogrammet"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"kutt lyden"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"slå på lyden"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrer"</string> @@ -869,6 +872,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Bytt til appen til høyre eller under mens du bruker delt skjerm"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Bytt til appen til venstre eller over mens du bruker delt skjerm"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"I delt skjerm: Bytt ut en app"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Skrivespråk"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"Bytt til neste språk"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"Bytt til forrige språk"</string> @@ -1411,15 +1416,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Tilpass"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Ferdig"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Vis-ikon"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"eller"</string> <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> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <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-nb/tiles_states_strings.xml b/packages/SystemUI/res/values-nb/tiles_states_strings.xml index a9efd1d95a7d..3ed1a4e4aa7a 100644 --- a/packages/SystemUI/res/values-nb/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-nb/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"Av"</item> <item msgid="3028994095749238254">"På"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml index 54644eb57c31..82f9aca68198 100644 --- a/packages/SystemUI/res/values-ne/strings.xml +++ b/packages/SystemUI/res/values-ne/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"इनपुट"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"हियरिङ डिभाइसहरू"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"सक्रिय गर्दै…"</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"अटो रोटेट"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"स्क्रिन स्वतःघुम्ने"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"लोकेसन"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"प्रिसेट अपडेट गर्न सकिएन"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"पूर्वनिर्धारित"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"लाइभ क्याप्सन"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"डिभाइसको माइक्रोफोन अनब्लक गर्ने हो?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"डिभाइसको क्यामेरा अनब्लक गर्ने हो?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"डिभाइसको क्यामेरा र माइक्रोफोन अनब्लक गर्ने हो?"</string> @@ -589,8 +593,7 @@ <string name="media_projection_action_text" msgid="3634906766918186440">"अहिले न"</string> <string name="empty_shade_text" msgid="8935967157319717412">"कुनै सूचनाहरू छैनन्"</string> <string name="no_unseen_notif_text" msgid="395512586119868682">"कुनै पनि नयाँ सूचना छैन"</string> - <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) --> - <skip /> + <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"नोटिफिकेसन कुलडाउन अहिले अन छ"</string> <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"तपाईंले एकै पटक धेरै नोटिफिकेसन प्राप्त गर्दा बढीमा २ मिनेटसम्म तपाईंको डिभाइसको भोल्युम र अलर्टहरूको सङ्ख्या स्वतः घटाइन्छ।"</string> <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"अफ गर्नुहोस्"</string> <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"पुराना सूचनाहरू हेर्न अनलक गर्नुहोस्"</string> @@ -698,8 +701,7 @@ <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> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"घण्टी बजाउने मोड"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"म्युट गर्नुहोस्"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"अनम्युट गर्नुहोस्"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"कम्पन गर्नुहोस्"</string> @@ -869,6 +871,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"स्प्लिट स्क्रिन प्रयोग गर्दै गर्दा दायाँ वा तलको एप चलाउनुहोस्"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"स्प्लिट स्क्रिन प्रयोग गर्दै गर्दा बायाँ वा माथिको एप चलाउनुहोस्"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"स्प्लिट स्क्रिन प्रयोग गरिएका बेला: एउटा स्क्रिनमा भएको एप अर्कोमा लैजानुहोस्"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"इनपुट"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"अर्को भाषा प्रयोग गर्नुहोस्"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"अघिल्लो भाषा प्रयोग गर्नुहोस्"</string> @@ -1411,15 +1415,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"कस्टमाइज गर्नुहोस्"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"पूरा भयो"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"\"एक्स्पान्ड गर्नुहोस्\" आइकन"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"वा"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ड्र्याग ह्यान्डल"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"किबोर्डसम्बन्धी सेटिङ"</string> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <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> @@ -1433,7 +1451,7 @@ <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"पछाडि जानुहोस्"</string> <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"तीन वटा औँला प्रयोग गरी टचप्याडमा बायाँ वा दायाँतिर स्वाइप गर्नुहोस्"</string> <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"राम्रो!"</string> - <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"तपाईंले \'पछाडि जानुहोस्\' नामक इसारा प्रयोग गर्ने तरिका सिक्नुभयो।"</string> + <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"तपाईंले जेस्चर प्रयोग गरी पछाडि जाने तरिका सिक्नुभएको छ।"</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"होमपेजमा जानुहोस्"</string> <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"टचप्याडमा तीन वटा औँलाले माथितिर स्वाइप गर्नुहोस्"</string> <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"अद्भुत!"</string> @@ -1441,11 +1459,11 @@ <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"हालसालै चलाइएका एपहरू हेर्नुहोस्"</string> <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"तीन वटा औँला प्रयोग गरी टचप्याडमा माथितिर स्वाइप गर्नुहोस् र होल्ड गर्नुहोस्"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"अद्भुत!"</string> - <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"तपाईंले हालसालै चलाइएका एपहरू हेर्ने जेस्चर पूरा गर्नुभएको छ।"</string> + <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"तपाईंले जेस्चर प्रयोग गरी हालसालै चलाइएका एपहरू हेर्ने तरिका सिक्नुभएको छ।"</string> <string name="tutorial_action_key_title" msgid="8172535792469008169">"सबै एपहरू हेर्नुहोस्"</string> <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"आफ्नो किबोर्डमा भएको एक्सन की थिच्नुहोस्"</string> <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"स्याबास!"</string> - <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"तपाईंले \"सबै एपहरू हेर्नुहोस्\" नामक जेस्चर प्रयोग गर्ने तरिका सिक्नुभयो"</string> + <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"तपाईंले जेस्चर प्रयोग गरी सबै एपहरू हेर्ने तरिका सिक्नुभएको छ"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"किबोर्ड ब्याकलाइट"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d मध्ये %1$d औँ स्तर"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"होम कन्ट्रोलहरू"</string> diff --git a/packages/SystemUI/res/values-ne/tiles_states_strings.xml b/packages/SystemUI/res/values-ne/tiles_states_strings.xml index c1b2f3420a40..2350c67d2bcf 100644 --- a/packages/SystemUI/res/values-ne/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-ne/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"अफ छ"</item> <item msgid="3028994095749238254">"अन छ"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml index 4c9eeb8d73ac..3e0b5d9d144d 100644 --- a/packages/SystemUI/res/values-nl/strings.xml +++ b/packages/SystemUI/res/values-nl/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Invoer"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Hoortoestellen"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Aanzetten…"</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automatisch draaien"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Scherm automatisch draaien"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Locatie"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"Kan voorinstelling niet updaten"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Voorinstelling"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Live ondertiteling"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Microfoon van apparaat niet meer blokkeren?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Apparaatcamera niet meer blokkeren?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Blokkeren van apparaatcamera en -microfoon opheffen?"</string> @@ -589,8 +593,7 @@ <string name="media_projection_action_text" msgid="3634906766918186440">"Nu starten"</string> <string name="empty_shade_text" msgid="8935967157319717412">"Geen meldingen"</string> <string name="no_unseen_notif_text" msgid="395512586119868682">"Geen nieuwe meldingen"</string> - <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) --> - <skip /> + <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"Afkoelperiode van meldingen staat nu aan"</string> <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Als je te veel meldingen tegelijk krijgt, worden het volume op je apparaat en meldingen automatisch maximaal 2 minuten beperkt."</string> <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Uitzetten"</string> <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Ontgrendel om oudere meldingen te zien"</string> @@ -868,6 +871,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Naar de app rechts of onderaan gaan als je een gesplitst scherm gebruikt"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Naar de app links of bovenaan gaan als je een gesplitst scherm gebruikt"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"Tijdens gesplitst scherm: een app vervangen door een andere"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Invoer"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"Overschakelen naar volgende taal"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"Overschakelen naar vorige taal"</string> @@ -1410,15 +1415,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Aanpassen"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Klaar"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icoon voor uitvouwen"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"of"</string> <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> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <skip /> <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> @@ -1435,11 +1454,11 @@ <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Je weet nu hoe je het gebaar voor terug maakt."</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Naar startscherm"</string> <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Swipe met 3 vingers omhoog op de touchpad"</string> - <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Goed werk!"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Goed gedaan!"</string> <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Je weet nu hoe je het gebaar Naar startscherm maakt"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Recente apps bekijken"</string> <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Swipe met 3 vingers omhoog en houd vast op de touchpad"</string> - <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Goed werk!"</string> + <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Goed gedaan!"</string> <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"Je weet nu hoe je het gebaar Recente apps bekijken maakt."</string> <string name="tutorial_action_key_title" msgid="8172535792469008169">"Alle apps bekijken"</string> <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"Druk op de actietoets op het toetsenbord"</string> diff --git a/packages/SystemUI/res/values-nl/tiles_states_strings.xml b/packages/SystemUI/res/values-nl/tiles_states_strings.xml index c5d93610fb87..4193463cae8e 100644 --- a/packages/SystemUI/res/values-nl/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-nl/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"Uit"</item> <item msgid="3028994095749238254">"Aan"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml index 7473ff58d995..980c47d13883 100644 --- a/packages/SystemUI/res/values-or/strings.xml +++ b/packages/SystemUI/res/values-or/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"ଇନପୁଟ୍"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"ଶ୍ରବଣ ଯନ୍ତ୍ର"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"ଅନ୍ ହେଉଛି…"</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ଅଟୋ-ରୋଟେଟ"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"ଅଟୋ-ରୋଟେଟ ସ୍କ୍ରିନ"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"ଲୋକେସନ"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"ପ୍ରିସେଟକୁ ଅପଡେଟ କରାଯାଇପାରିଲା ନାହିଁ"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"ପ୍ରିସେଟ"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"ଲାଇଭ କେପ୍ସନ"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ଡିଭାଇସର ମାଇକ୍ରୋଫୋନକୁ ଅନବ୍ଲକ କରିବେ?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"ଡିଭାଇସର କେମେରାକୁ ଅନବ୍ଲକ କରିବେ?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"ଡିଭାଇସର କ୍ୟାମେରା ଏବଂ ମାଇକ୍ରୋଫୋନକୁ ଅନବ୍ଲକ୍ କରିବେ?"</string> @@ -698,8 +702,7 @@ <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> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"ରିଂଗର ମୋଡ"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"ମ୍ୟୁଟ"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ଅନ୍-ମ୍ୟୁଟ୍ କରନ୍ତୁ"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"ଭାଇବ୍ରେଟ୍"</string> @@ -821,7 +824,7 @@ <string name="keyboard_key_page_up" msgid="173914303254199845">"ଉପର ପୃଷ୍ଠା"</string> <string name="keyboard_key_page_down" msgid="9035902490071829731">"ତଳ ପୃଷ୍ଠା"</string> <string name="keyboard_key_forward_del" msgid="5325501825762733459">"ଡିଲିଟ କରନ୍ତୁ"</string> - <string name="keyboard_key_esc" msgid="6230365950511411322">"ଏସକେପ"</string> + <string name="keyboard_key_esc" msgid="6230365950511411322">"Esc"</string> <string name="keyboard_key_move_home" msgid="3496502501803911971">"ହୋମ"</string> <string name="keyboard_key_move_end" msgid="99190401463834854">"ସମାପ୍ତ"</string> <string name="keyboard_key_insert" msgid="4621692715704410493">"ଇନ୍ସର୍ଟ"</string> @@ -869,6 +872,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"ସ୍ପ୍ଲିଟ ସ୍କ୍ରିନ ବ୍ୟବହାର କରିବା ସମୟରେ ଡାହାଣପଟର ବା ତଳର ଆପକୁ ସୁଇଚ କରନ୍ତୁ"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"ସ୍ପ୍ଲିଟ ସ୍କ୍ରିନ ବ୍ୟବହାର କରିବା ସମୟରେ ବାମପଟର ବା ଉପରର ଆପକୁ ସୁଇଚ କରନ୍ତୁ"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"ସ୍ପ୍ଲିଟ ସ୍କ୍ରିନ ସମୟରେ: କୌଣସି ଆପକୁ ଗୋଟିଏରୁ ଅନ୍ୟ ଏକ ଆପରେ ବଦଳାନ୍ତୁ"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"ଇନପୁଟ"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"ପରବର୍ତ୍ତୀ ଭାଷାକୁ ସୁଇଚ କରନ୍ତୁ"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"ପୂର୍ବବର୍ତ୍ତୀ ଭାଷାକୁ ସୁଇଚ କରନ୍ତୁ"</string> @@ -881,7 +886,7 @@ <string name="keyboard_shortcut_group_applications_email" msgid="7852376788894975192">"ଇମେଲ୍"</string> <string name="keyboard_shortcut_group_applications_sms" msgid="6912633831752843566">"SMS"</string> <string name="keyboard_shortcut_group_applications_music" msgid="9032078456666204025">"ମ୍ୟୁଜିକ୍"</string> - <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"କ୍ୟାଲେଣ୍ଡର"</string> + <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Calendar"</string> <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"କାଲକୁଲେଟର"</string> <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Maps"</string> <string name="volume_and_do_not_disturb" msgid="502044092739382832">"ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ"</string> @@ -1411,15 +1416,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"କଷ୍ଟମାଇଜ କରନ୍ତୁ"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"ହୋଇଗଲା"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ଆଇକନକୁ ବିସ୍ତାର କରନ୍ତୁ"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"କିମ୍ବା"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ଡ୍ରାଗ ହେଣ୍ଡେଲ"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"କୀବୋର୍ଡ ସେଟିଂ"</string> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <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-or/tiles_states_strings.xml b/packages/SystemUI/res/values-or/tiles_states_strings.xml index fe187c2ff082..35afd612e6b6 100644 --- a/packages/SystemUI/res/values-or/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-or/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"ବନ୍ଦ ଅଛି"</item> <item msgid="3028994095749238254">"ଚାଲୁ ଅଛି"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml index 700bb1ce3fd8..8cc9f6e57939 100644 --- a/packages/SystemUI/res/values-pa/strings.xml +++ b/packages/SystemUI/res/values-pa/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"ਇਨਪੁੱਟ"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"ਸੁਣਨ ਦੇ ਸਾਧਨ"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"ਚਾਲੂ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ…"</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ਸਵੈ-ਘੁਮਾਓ"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"ਸਕ੍ਰੀਨ ਨੂੰ ਆਪਣੇ ਆਪ ਘੁੰਮਾਓ"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"ਟਿਕਾਣਾ"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"ਪ੍ਰੀਸੈੱਟ ਨੂੰ ਅੱਪਡੇਟ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"ਪ੍ਰੀਸੈੱਟ"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"ਲਾਈਵ ਸੁਰਖੀਆਂ"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ਕੀ ਡੀਵਾਈਸ ਦੇ ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਨੂੰ ਅਣਬਲਾਕ ਕਰਨਾ ਹੈ?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"ਕੀ ਡੀਵਾਈਸ ਦੇ ਕੈਮਰੇ ਨੂੰ ਅਣਬਲਾਕ ਕਰਨਾ ਹੈ?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"ਕੀ ਡੀਵਾਈਸ ਦੇ ਕੈਮਰੇ ਅਤੇ ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਨੂੰ ਅਣਬਲਾਕ ਕਰਨਾ ਹੈ?"</string> @@ -698,8 +702,7 @@ <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> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"ਰਿੰਗਰ ਮੋਡ"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"ਮਿਊਟ ਕਰੋ"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ਅਣਮਿਊਟ ਕਰੋ"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"ਥਰਥਰਾਹਟ"</string> @@ -869,6 +872,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"ਸਪਲਿਟ ਸਕ੍ਰੀਨ ਦੀ ਵਰਤੋਂ ਕਰਨ ਵੇਲੇ ਸੱਜੇ ਜਾਂ ਹੇਠਾਂ ਮੌਜੂਦ ਐਪ \'ਤੇ ਸਵਿੱਚ ਕਰੋ"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"ਸਪਲਿਟ ਸਕ੍ਰੀਨ ਦੀ ਵਰਤੋਂ ਕਰਨ ਵੇਲੇ ਖੱਬੇ ਜਾਂ ਉੱਪਰ ਮੌਜੂਦ ਐਪ \'ਤੇ ਸਵਿੱਚ ਕਰੋ"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"ਸਪਲਿਟ ਸਕ੍ਰੀਨ ਦੌਰਾਨ: ਇੱਕ ਐਪ ਨਾਲ ਦੂਜੀ ਐਪ ਨੂੰ ਬਦਲੋ"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"ਇਨਪੁੱਟ"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"ਅਗਲੀ ਭਾਸ਼ਾ \'ਤੇ ਸਵਿੱਚ ਕਰੋ"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"ਪਿਛਲੀ ਭਾਸ਼ਾ \'ਤੇ ਸਵਿੱਚ ਕਰੋ"</string> @@ -1411,15 +1416,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"ਵਿਉਂਤਬੱਧ ਕਰੋ"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"ਹੋ ਗਿਆ"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ਪ੍ਰਤੀਕ ਦਾ ਵਿਸਤਾਰ ਕਰੋ"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ਜਾਂ"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ਘਸੀਟਣ ਵਾਲਾ ਹੈਂਡਲ"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"ਕੀ-ਬੋਰਡ ਸੈਟਿੰਗਾਂ"</string> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <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/tiles_states_strings.xml b/packages/SystemUI/res/values-pa/tiles_states_strings.xml index 62dc05ad67bb..ab2f8abdd739 100644 --- a/packages/SystemUI/res/values-pa/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-pa/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"ਬੰਦ ਹੈ"</item> <item msgid="3028994095749238254">"ਚਾਲੂ ਹੈ"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml index 9b03958593d3..c183116e3528 100644 --- a/packages/SystemUI/res/values-pl/strings.xml +++ b/packages/SystemUI/res/values-pl/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Wejście"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Aparaty słuchowe"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Włączam…"</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Autoobracanie"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Autoobracanie ekranu"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Lokalizacja"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"Nie udało się zaktualizować gotowego ustawienia"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Gotowe ustawienie"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Napisy na żywo"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Odblokować mikrofon urządzenia?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Odblokować aparat urządzenia?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Odblokować aparat i mikrofon urządzenia?"</string> @@ -589,8 +593,7 @@ <string name="media_projection_action_text" msgid="3634906766918186440">"Rozpocznij teraz"</string> <string name="empty_shade_text" msgid="8935967157319717412">"Brak powiadomień"</string> <string name="no_unseen_notif_text" msgid="395512586119868682">"Brak nowych powiadomień"</string> - <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) --> - <skip /> + <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"Wyciszanie powiadomień jest teraz włączone"</string> <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Gdy w krótkim czasie otrzymasz za dużo powiadomień, dźwięki zostaną automatycznie wyciszone na maks. 2 min."</string> <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Wyłącz"</string> <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Odblokuj i zobacz starsze powiadomienia"</string> @@ -698,8 +701,7 @@ <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Stały"</string> <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Śledzenie głowy"</string> <string name="volume_ringer_change" msgid="3574969197796055532">"Kliknij, aby zmienić tryb dzwonka"</string> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"tryb dzwonka"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"wycisz"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"wyłącz wyciszenie"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"włącz wibracje"</string> @@ -869,6 +871,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Przełącz się na aplikację po prawej lub poniżej na podzielonym ekranie"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Przełącz się na aplikację po lewej lub powyżej na podzielonym ekranie"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"Podczas podzielonego ekranu: zastępowanie aplikacji"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Wprowadzanie"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"Przełącz na następny język"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"Przełącz na poprzedni język"</string> @@ -1411,15 +1415,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Dostosuj"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Gotowe"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona rozwijania"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"lub"</string> <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> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <skip /> <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> @@ -1468,7 +1486,7 @@ <string name="all_apps_edu_notification_content" msgid="3255070575694025585">"Naciśnij klawisz działania w dowolnym momencie. Kliknij, aby poznać więcej gestów."</string> <string name="accessibility_deprecate_extra_dim_dialog_title" msgid="910988771011857460">"Dodatkowe przyciemnienie jest teraz częścią suwaka jasności"</string> <string name="accessibility_deprecate_extra_dim_dialog_description" msgid="4453123359258743230">"Możesz teraz dodatkowo przyciemnić ekran, jeszcze bardziej zmniejszając poziom jasności.\n\nTa funkcja jest teraz częścią suwaka jasności, więc skróty do dodatkowego przyciemniania zostaną usunięte."</string> - <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"Usuń skróty do dodatkowego przyciemnienia"</string> + <string name="accessibility_deprecate_extra_dim_dialog_button" msgid="3947537827396916005">"Usuń skróty do dodatkowego przyciemniania"</string> <string name="accessibility_deprecate_extra_dim_dialog_toast" msgid="165474092660941104">"Skróty do dodatkowego przyciemnienia zostały usunięte"</string> <string name="qs_edit_mode_category_connectivity" msgid="4559726936546032672">"Łączność"</string> <string name="qs_edit_mode_category_accessibility" msgid="7969091385071475922">"Ułatwienia dostępu"</string> diff --git a/packages/SystemUI/res/values-pl/tiles_states_strings.xml b/packages/SystemUI/res/values-pl/tiles_states_strings.xml index 5aa719f2ea75..d3191b0f6631 100644 --- a/packages/SystemUI/res/values-pl/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-pl/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"Wyłączono"</item> <item msgid="3028994095749238254">"Włączone"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml index e882cb852a02..f1dd9a4b27b1 100644 --- a/packages/SystemUI/res/values-pt-rBR/strings.xml +++ b/packages/SystemUI/res/values-pt-rBR/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Entrada"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Aparelhos auditivos"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Ativando…"</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Giro automático"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Giro automático da tela"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Localização"</string> @@ -404,7 +406,7 @@ <string name="custom" msgid="3337456985275158299">"Personalizado"</string> <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Configurações de rastreamento personalizado"</string> <string name="restore_default" msgid="5259420807486239755">"Restaurar padrão"</string> - <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modo uma mão"</string> + <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modo para uma mão"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Aparelhos auditivos"</string> <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Ativos"</string> <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Desconectados"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"Não foi possível atualizar a predefinição"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Predefinição"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Legenda instantânea"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Desbloquear o microfone do dispositivo?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Desbloquear a câmera do dispositivo?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Desbloquear a câmera e o microfone do dispositivo?"</string> @@ -698,8 +702,7 @@ <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Fixo"</string> <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Rastreamento de cabeça"</string> <string name="volume_ringer_change" msgid="3574969197796055532">"Toque para mudar o modo da campainha"</string> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"modo da campainha"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"desativar o som"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ativar o som"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrar"</string> @@ -809,7 +812,7 @@ <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Voltar"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> - <string name="keyboard_key_space" msgid="6980847564173394012">"Space"</string> + <string name="keyboard_key_space" msgid="6980847564173394012">"Espaço"</string> <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string> <string name="keyboard_key_backspace" msgid="4095278312039628074">"Barra de espaço"</string> <string name="keyboard_key_media_play_pause" msgid="8389984232732277478">"Reproduzir/pausar"</string> @@ -869,6 +872,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Mudar para o app à direita ou abaixo ao usar a tela dividida"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Mudar para o app à esquerda ou acima ao usar a tela dividida"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"Com a tela dividida: substituir um app por outro"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Entrada"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"Mudar para o próximo idioma"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"Mudar para o idioma anterior"</string> @@ -1411,15 +1416,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Personalizar"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Concluir"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ícone \"Abrir\""</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string> <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> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <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-rBR/tiles_states_strings.xml b/packages/SystemUI/res/values-pt-rBR/tiles_states_strings.xml index 3526c77d1e24..e315bad6bf80 100644 --- a/packages/SystemUI/res/values-pt-rBR/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-pt-rBR/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"Desativar"</item> <item msgid="3028994095749238254">"Ativar"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml index f1bae0e4113c..03c32a834721 100644 --- a/packages/SystemUI/res/values-pt-rPT/strings.xml +++ b/packages/SystemUI/res/values-pt-rPT/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Entrada"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Aparelhos auditivos"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"A ativar..."</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Rotação auto."</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Rodar o ecrã automaticamente"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Localização"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"Não foi possível atualizar a predefinição"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Predefinição"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Legendas instantâneas"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Desbloquear o microfone do dispositivo?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Desbloquear a câmara do dispositivo?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Quer desbloquear a câmara e o microfone?"</string> @@ -589,8 +593,7 @@ <string name="media_projection_action_text" msgid="3634906766918186440">"Começar agora"</string> <string name="empty_shade_text" msgid="8935967157319717412">"Sem notificações"</string> <string name="no_unseen_notif_text" msgid="395512586119868682">"Não existem novas notificações"</string> - <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) --> - <skip /> + <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"O repouso das notificações está agora ativado"</string> <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"O volume e os alertas são reduzidos automaticamente durante até 2 minutos quando recebe muitas notificações de uma vez."</string> <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Desativar"</string> <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Desbloqueie e veja notificações antigas"</string> @@ -868,6 +871,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Mudar para a app à direita ou abaixo enquanto usa o ecrã dividido"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Mude para a app à esquerda ou acima enquanto usa o ecrã dividido"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"Durante o ecrã dividido: substituir uma app por outra"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Entrada"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"Mudar para idioma seguinte"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"Mudar para idioma anterior"</string> @@ -1410,15 +1415,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Personalizar"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Concluir"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ícone de expandir"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string> <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> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <skip /> <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-rPT/tiles_states_strings.xml b/packages/SystemUI/res/values-pt-rPT/tiles_states_strings.xml index 34a5ed7b2ca9..deb6783e1b23 100644 --- a/packages/SystemUI/res/values-pt-rPT/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-pt-rPT/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"Desativados"</item> <item msgid="3028994095749238254">"Ativados"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml index e882cb852a02..f1dd9a4b27b1 100644 --- a/packages/SystemUI/res/values-pt/strings.xml +++ b/packages/SystemUI/res/values-pt/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Entrada"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Aparelhos auditivos"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Ativando…"</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Giro automático"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Giro automático da tela"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Localização"</string> @@ -404,7 +406,7 @@ <string name="custom" msgid="3337456985275158299">"Personalizado"</string> <string name="custom_trace_settings_dialog_title" msgid="2608570500144830554">"Configurações de rastreamento personalizado"</string> <string name="restore_default" msgid="5259420807486239755">"Restaurar padrão"</string> - <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modo uma mão"</string> + <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modo para uma mão"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Aparelhos auditivos"</string> <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Ativos"</string> <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Desconectados"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"Não foi possível atualizar a predefinição"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Predefinição"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Legenda instantânea"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Desbloquear o microfone do dispositivo?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Desbloquear a câmera do dispositivo?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Desbloquear a câmera e o microfone do dispositivo?"</string> @@ -698,8 +702,7 @@ <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Fixo"</string> <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Rastreamento de cabeça"</string> <string name="volume_ringer_change" msgid="3574969197796055532">"Toque para mudar o modo da campainha"</string> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"modo da campainha"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"desativar o som"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ativar o som"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrar"</string> @@ -809,7 +812,7 @@ <string name="keyboard_key_home" msgid="3734400625170020657">"Home"</string> <string name="keyboard_key_back" msgid="4185420465469481999">"Voltar"</string> <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string> - <string name="keyboard_key_space" msgid="6980847564173394012">"Space"</string> + <string name="keyboard_key_space" msgid="6980847564173394012">"Espaço"</string> <string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string> <string name="keyboard_key_backspace" msgid="4095278312039628074">"Barra de espaço"</string> <string name="keyboard_key_media_play_pause" msgid="8389984232732277478">"Reproduzir/pausar"</string> @@ -869,6 +872,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Mudar para o app à direita ou abaixo ao usar a tela dividida"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Mudar para o app à esquerda ou acima ao usar a tela dividida"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"Com a tela dividida: substituir um app por outro"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Entrada"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"Mudar para o próximo idioma"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"Mudar para o idioma anterior"</string> @@ -1411,15 +1416,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Personalizar"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Concluir"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ícone \"Abrir\""</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string> <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> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <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/tiles_states_strings.xml b/packages/SystemUI/res/values-pt/tiles_states_strings.xml index 3526c77d1e24..e315bad6bf80 100644 --- a/packages/SystemUI/res/values-pt/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-pt/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"Desativar"</item> <item msgid="3028994095749238254">"Ativar"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml index 18cb139ae6cf..5b3190fef48c 100644 --- a/packages/SystemUI/res/values-ro/strings.xml +++ b/packages/SystemUI/res/values-ro/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Intrare"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Aparate auditive"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Se activează..."</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Rotire automată"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Rotirea automată a ecranului"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Locație"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"Nu s-a putut actualiza presetarea"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Presetare"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Subtitrări live"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Deblochezi microfonul dispozitivului?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Deblochezi camera dispozitivului?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Deblochezi camera și microfonul dispozitivului?"</string> @@ -698,8 +702,7 @@ <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Fix"</string> <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Urmărirea mișcărilor capului"</string> <string name="volume_ringer_change" msgid="3574969197796055532">"Atinge pentru a schimba modul soneriei"</string> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"modul sonerie"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"dezactivează sunetul"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"activează sunetul"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrații"</string> @@ -869,6 +872,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Treci la aplicația din dreapta sau de mai jos cu ecranul împărțit"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Treci la aplicația din stânga sau de mai sus cu ecranul împărțit"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"În modul ecran împărțit: înlocuiește o aplicație cu alta"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Introducere"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"Comută la următoarea limbă"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"Comută la limba anterioară"</string> @@ -1411,15 +1416,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Personalizează"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Gata"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Pictograma de extindere"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"sau"</string> <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> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <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> @@ -1437,7 +1456,7 @@ <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Înapoi la pagina de pornire"</string> <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Glisează în sus cu trei degete oriunde pe touchpad"</string> <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Excelent!"</string> - <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Ai finalizat gestul „accesează ecranul de pornire”"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Ai finalizat gestul „înapoi la pagina de pornire”"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Vezi aplicațiile recente"</string> <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Glisează în sus și ține apăsat cu trei degete pe touchpad"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"Excelent!"</string> diff --git a/packages/SystemUI/res/values-ro/tiles_states_strings.xml b/packages/SystemUI/res/values-ro/tiles_states_strings.xml index a68f1408dd83..fa950c30850a 100644 --- a/packages/SystemUI/res/values-ro/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-ro/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"Dezactivat"</item> <item msgid="3028994095749238254">"Activat"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml index ffab1aff11f1..665bd26a386e 100644 --- a/packages/SystemUI/res/values-ru/strings.xml +++ b/packages/SystemUI/res/values-ru/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Устройство ввода"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Слуховые аппараты"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Включение…"</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Автоповорот"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Автоповорот экрана"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Геолокация"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"Не удалось обновить набор настроек."</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Набор настроек"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Автоматические субтитры"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Разблокировать микрофон устройства?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Разблокировать камеру устройства?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Разблокировать камеру и микрофон устройства?"</string> @@ -698,8 +702,7 @@ <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> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"режим звонка"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"отключить звук"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"включить звук"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"включить вибрацию"</string> @@ -869,6 +872,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Перейти к приложению справа или внизу на разделенном экране"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Перейти к приложению слева или вверху на разделенном экране"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"В режиме разделения экрана заменить одно приложение другим"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Ввод"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"Выбрать следующий язык"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"Выбрать предыдущий язык"</string> @@ -1411,15 +1416,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Настроить"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Готово"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Значок \"Развернуть\""</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"или"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Маркер перемещения"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Настройки клавиатуры"</string> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <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> @@ -1427,8 +1446,8 @@ <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> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"На главный экран"</string> + <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Просмотр недавних приложений"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Готово"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Назад"</string> <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Проведите тремя пальцами влево или вправо по сенсорной панели."</string> @@ -1436,7 +1455,7 @@ <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Вы выполнили жест для перехода назад."</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"На главный экран"</string> <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Проведите тремя пальцами вверх по сенсорной панели."</string> - <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Отличная работа!"</string> + <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Отлично!"</string> <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Вы выполнили жест для перехода на главный экран."</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"Просмотр недавних приложений"</string> <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"Проведите вверх по сенсорной панели тремя пальцами и удерживайте."</string> diff --git a/packages/SystemUI/res/values-ru/tiles_states_strings.xml b/packages/SystemUI/res/values-ru/tiles_states_strings.xml index 592937c7b9f6..cd12baeadbb2 100644 --- a/packages/SystemUI/res/values-ru/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-ru/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"Отключены"</item> <item msgid="3028994095749238254">"Включены"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml index 1b2a2e47ebc2..0df5d4494e67 100644 --- a/packages/SystemUI/res/values-si/strings.xml +++ b/packages/SystemUI/res/values-si/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"ආදානය"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"ශ්රවණාධාරක"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"ක්රියාත්මක කරමින්…"</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ස්වයංක්රීය කරකැවීම"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"ස්වයංක්රීයව-භ්රමණය වන තිරය"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"ස්ථානය"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"පෙර සැකසීම යාවත්කාලීන කළ නොහැකි විය"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"පෙරසැකසුම"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"සජීවී සිරස්තල"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"උපාංග මයික්රෆෝනය අවහිර කිරීම ඉවත් කරන්නද?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"උපාංග කැමරාව අවහිර කිරීම ඉවත් කරන්නද?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"උපාංග කැමරාව සහ මයික්රෆෝනය අවහිර කිරීම ඉවත් කරන්නද?"</string> @@ -698,8 +702,7 @@ <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> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"හඬ නඟන ආකාරය"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"නිහඬ කරන්න"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"නිශ්ශබ්දතාවය ඉවත් කරන්න"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"කම්පනය"</string> @@ -869,6 +872,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"බෙදුම් තිරය භාවිත කරන අතරතුර දකුණේ හෝ පහළින් ඇති යෙදුමට මාරු වන්න"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"බෙදුම් තිරය භාවිත කරන අතරතුර වමේ හෝ ඉහළ ඇති යෙදුමට මාරු වන්න"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"බෙදුම් තිරය අතරතුර: යෙදුමක් එකකින් තවත් එකක් ප්රතිස්ථාපනය කරන්න"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"ආදානය"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"මීළඟ භාෂාවට මාරු වන්න"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"පෙර භාෂාවට මාරු වන්න"</string> @@ -1411,15 +1416,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"අභිරුචිකරණය කරන්න"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"නිමයි"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"දිගහැරීම් නිරූපකය"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"හෝ"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"ඇදීම් හැඬලය"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"යතුරු පුවරු සැකසීම්"</string> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <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-si/tiles_states_strings.xml b/packages/SystemUI/res/values-si/tiles_states_strings.xml index 681f3d52bc09..595575d7eebc 100644 --- a/packages/SystemUI/res/values-si/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-si/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"ක්රියාවිරහිතයි"</item> <item msgid="3028994095749238254">"ක්රියාත්මකයි"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml index 2e03aab20730..7331ba015726 100644 --- a/packages/SystemUI/res/values-sk/strings.xml +++ b/packages/SystemUI/res/values-sk/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Vstup"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Načúvadlá"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Zapína sa…"</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automatické otáčanie"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Automatické otáčanie obrazovky"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Poloha"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"Predvoľbu sa nepodarilo aktualizovať"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Predvoľba"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Živý prepis"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Chcete odblokovať mikrofón zariadenia?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Chcete odblokovať kameru zariadenia?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Chcete odblokovať fotoaparát a mikrofón zariadenia?"</string> @@ -698,8 +702,7 @@ <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Pevné"</string> <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Sled. polohy hlavy"</string> <string name="volume_ringer_change" msgid="3574969197796055532">"Režim zvonenia zmeníte klepnutím"</string> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"režim zvonenia"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"vypnite zvuk"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"zapnite zvuk"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"zapnite vibrovanie"</string> @@ -869,6 +872,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Prechod na aplikáciu vpravo alebo dole pri rozdelenej obrazovke"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Prechod na aplikáciu vľavo alebo hore pri rozdelenej obrazovke"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"Počas rozdelenej obrazovky: nahradenie aplikácie inou"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Vstup"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"Prepnutie na ďalší jazyk"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"Prepnutie na predchádzajúci jazyk"</string> @@ -1411,15 +1416,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Prispôsobiť"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Hotovo"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona rozbalenia"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"alebo"</string> <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> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <skip /> <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Prechádzajte 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">"Prechádzajte pomocou touchpadu"</string> diff --git a/packages/SystemUI/res/values-sk/tiles_states_strings.xml b/packages/SystemUI/res/values-sk/tiles_states_strings.xml index 6b5af805f15b..607c2215642b 100644 --- a/packages/SystemUI/res/values-sk/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-sk/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"Vypnuté"</item> <item msgid="3028994095749238254">"Zapnuté"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml index ad71a77d5361..d0b0a9ddd0a3 100644 --- a/packages/SystemUI/res/values-sl/strings.xml +++ b/packages/SystemUI/res/values-sl/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Vhodna naprava"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Slušni aparati"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Vklapljanje …"</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Samodejno sukanje"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Samodejno sukanje zaslona"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Lokacija"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"Prednastavljenih vrednosti ni bilo mogoče posodobiti"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Prednastavljeno"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Samodejni podnapisi"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Želite odblokirati mikrofon v napravi?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Želite odblokirati fotoaparat v napravi?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Želite odblokirati fotoaparat in mikrofon v napravi?"</string> @@ -589,8 +593,7 @@ <string name="media_projection_action_text" msgid="3634906766918186440">"Začni zdaj"</string> <string name="empty_shade_text" msgid="8935967157319717412">"Ni obvestil"</string> <string name="no_unseen_notif_text" msgid="395512586119868682">"Ni novih obvestil"</string> - <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) --> - <skip /> + <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"Utišanje obvestil je zdaj vklopljeno"</string> <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Ko prejmete preveč obvestil naenkrat, se glasnost naprave in opozoril samodejno zmanjša za največ 2 minuti."</string> <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Izklopi"</string> <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Odklenite za ogled starejših obvestil"</string> @@ -698,8 +701,7 @@ <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Fiksno"</string> <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Spremljanje premikov glave"</string> <string name="volume_ringer_change" msgid="3574969197796055532">"Dotaknite se, če želite spremeniti način zvonjenja."</string> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"način zvonjenja"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"izklop zvoka"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"vklop zvoka"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibriranje"</string> @@ -869,6 +871,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Preklop na aplikacijo desno ali spodaj med uporabo razdeljenega zaslona"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Preklop na aplikacijo levo ali zgoraj med uporabo razdeljenega zaslona"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"Pri razdeljenem zaslonu: medsebojna zamenjava aplikacij"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Vnos"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"Preklop na naslednji jezik"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"Preklop na prejšnji jezik"</string> @@ -1411,23 +1415,37 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Prilagodi"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Končano"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona za razširitev"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ali"</string> <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> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <skip /> <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> <string name="launch_touchpad_tutorial_notification_content" msgid="7931085031240753226">"Naučite se uporabljati poteze na sledilni ploščici."</string> <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Krmarjenje s tipkovnico in sledilno ploščico"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Učenje potez na sledilni ploščici, bližnjičnih tipk in drugega"</string> - <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Nazaj"</string> - <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Pojdi na začetni zaslon"</string> + <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Pomik nazaj"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Pomik na začetni zaslon"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Ogled nedavnih aplikacij"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Končano"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Nazaj"</string> diff --git a/packages/SystemUI/res/values-sl/tiles_states_strings.xml b/packages/SystemUI/res/values-sl/tiles_states_strings.xml index 5f60ffdaebf7..fddaea615bab 100644 --- a/packages/SystemUI/res/values-sl/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-sl/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"Izklopljeno"</item> <item msgid="3028994095749238254">"Vklopljeno"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml index 027636f7f955..428caf08c81d 100644 --- a/packages/SystemUI/res/values-sq/strings.xml +++ b/packages/SystemUI/res/values-sq/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Hyrja"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Aparatet e dëgjimit"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Po aktivizohet…"</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Rrotullim automatik"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Rrotullimi automatik i ekranit"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Vendndodhja"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"Paravendosja nuk mund të përditësohej"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Paravendosja"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Titrat në çast"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Të zhbllokohet mikrofoni i pajisjes?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Të zhbllokohet kamera e pajisjes?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Të zhbllokohen kamera dhe mikrofoni i pajisjes?"</string> @@ -698,8 +702,7 @@ <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"E fiksuar"</string> <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Ndjekja e lëvizjeve të kokës"</string> <string name="volume_ringer_change" msgid="3574969197796055532">"Trokit për të ndryshuar modalitetin e ziles"</string> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"modaliteti i ziles"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"çaktivizo audion"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"aktivizo audion"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"lësho dridhje"</string> @@ -869,6 +872,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Kalo tek aplikacioni djathtas ose poshtë kur përdor ekranin e ndarë"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Kalo tek aplikacioni në të majtë ose sipër kur përdor ekranin e ndarë"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"Gjatë ekranit të ndarë: zëvendëso një aplikacion me një tjetër"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Hyrja"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"Kalo te gjuha tjetër"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"Kalo te gjuha e mëparshme"</string> @@ -1411,15 +1416,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Personalizo"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"U krye"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona e zgjerimit"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ose"</string> <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> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <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-sq/tiles_states_strings.xml b/packages/SystemUI/res/values-sq/tiles_states_strings.xml index 9b5032eecbd8..b30c8e7847de 100644 --- a/packages/SystemUI/res/values-sq/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-sq/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"Joaktive"</item> <item msgid="3028994095749238254">"Aktive"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml index bc3a0503b58a..63b3e9a4f38d 100644 --- a/packages/SystemUI/res/values-sr/strings.xml +++ b/packages/SystemUI/res/values-sr/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Унос"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Слушни апарати"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Укључује се..."</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Аутоматска ротација"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Аутоматско ротирање екрана"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Локација"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"Ажурирање задатих подешавања није успело"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Унапред одређена подешавања"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Титл уживо"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Желите да одблокирате микрофон уређаја?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Желите да одблокирате камеру уређаја?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Желите да одблокирате камеру и микрофон уређаја?"</string> @@ -589,8 +593,7 @@ <string name="media_projection_action_text" msgid="3634906766918186440">"Започни"</string> <string name="empty_shade_text" msgid="8935967157319717412">"Нема обавештења"</string> <string name="no_unseen_notif_text" msgid="395512586119868682">"Нема нових обавештења"</string> - <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) --> - <skip /> + <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"Утишавање обавештења је сада укључено"</string> <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Звук и број упозорења на уређају се аутоматски смањују на 2 минута када добијете превише обавештења."</string> <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Искључи"</string> <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Откључајте за старија обавештења"</string> @@ -698,8 +701,7 @@ <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> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"режим звона"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"искључите звук"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"укључите звук"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"вибрација"</string> @@ -869,6 +871,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Пређи у апликацију здесна или испод док је подељен екран"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Пређите у апликацију слева или изнад док користите подељени екран"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"У режиму подељеног екрана: замена једне апликације другом"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Унос"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"Пређи на следећи језик"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"Пређи на претходни језик"</string> @@ -1411,15 +1415,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Прилагоди"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Готово"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Икона за проширивање"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"или"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Маркер за превлачење"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Подешавања тастатуре"</string> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <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-sr/tiles_states_strings.xml b/packages/SystemUI/res/values-sr/tiles_states_strings.xml index 2acf1d264213..2a2e07459243 100644 --- a/packages/SystemUI/res/values-sr/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-sr/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"Искључено"</item> <item msgid="3028994095749238254">"Укључено"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml index 66b585178f76..bffd40b6c5fe 100644 --- a/packages/SystemUI/res/values-sv/strings.xml +++ b/packages/SystemUI/res/values-sv/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Ingång"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Hörapparater"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Aktiverar …"</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Rotera automatiskt"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Rotera skärmen automatiskt"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Plats"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"Det gick inte att uppdatera förinställningen"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Förinställning"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Live Caption"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Vill du återaktivera enhetens mikrofon?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Vill du återaktivera enhetens kamera?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Vill du återaktivera enhetens kamera och mikrofon?"</string> @@ -698,8 +702,7 @@ <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Statiskt"</string> <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Huvudspårning"</string> <string name="volume_ringer_change" msgid="3574969197796055532">"Tryck för att ändra ringsignalens läge"</string> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"ringsignalläge"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"stänga av ljudet"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"slå på ljudet"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibration"</string> @@ -869,6 +872,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Byt till appen till höger eller nedanför när du använder delad skärm"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Byt till appen till vänster eller ovanför när du använder delad skärm"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"Med delad skärm: ersätt en app med en annan"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Inmatning"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"Byt till nästa språk"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"Byt till föregående språk"</string> @@ -1411,15 +1416,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Anpassa"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Klar"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikonen Utöka"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"eller"</string> <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> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <skip /> <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-sv/tiles_states_strings.xml b/packages/SystemUI/res/values-sv/tiles_states_strings.xml index cf49f8db2606..b72f404c710f 100644 --- a/packages/SystemUI/res/values-sv/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-sv/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"Av"</item> <item msgid="3028994095749238254">"På"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml index 67abd41850ea..2140a8ae06bb 100644 --- a/packages/SystemUI/res/values-sw/strings.xml +++ b/packages/SystemUI/res/values-sw/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Vifaa vya kuingiza sauti"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Visaidizi vya kusikia"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Inawasha..."</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Zungusha kiotomatiki"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Skrini ijizungushe kiotomatiki"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Mahali"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"Imeshindwa kusasisha mipangilio iliyowekwa mapema"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Mipangilio iliyowekwa mapema"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Manukuu Papo Hapo"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Ungependa kuwacha kuzuia maikrofoni ya kifaa?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Ungependa kuacha kuzuia kamera ya kifaa?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Ungependa kuwacha kuzuia kamera na maikrofoni ya kifaa?"</string> @@ -698,8 +702,7 @@ <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Imerekebishwa"</string> <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Ufuatilizi wa Kichwa"</string> <string name="volume_ringer_change" msgid="3574969197796055532">"Gusa ili ubadilishe hali ya programu inayotoa milio ya simu"</string> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"hali ya programu inayotoa milio ya simu"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"zima sauti"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"washa sauti"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"tetema"</string> @@ -869,6 +872,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Badilisha ili uende kwenye programu iliyo kulia au chini unapotumia hali ya kugawa skrini"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Badilisha uende kwenye programu iliyo kushoto au juu unapotumia hali ya kugawa skrini"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"Ukigawanya skrini: badilisha kutoka programu moja hadi nyingine"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Vifaa vya kuingiza data"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"Badilisha utumie lugha inayofuata"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"Badilisha utumie lugha iliyotangulia"</string> @@ -1411,15 +1416,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Weka mapendeleo"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Nimemaliza"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Panua aikoni"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"au"</string> <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> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <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">"Jifunze kuhusu mikato ya kibodi"</string> <string name="launch_touchpad_tutorial_notification_title" msgid="2243780062772196901">"Kusogeza kwa kutumia padi yako ya kugusa"</string> diff --git a/packages/SystemUI/res/values-sw/tiles_states_strings.xml b/packages/SystemUI/res/values-sw/tiles_states_strings.xml index 15de7f88694c..4de75caf05ae 100644 --- a/packages/SystemUI/res/values-sw/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-sw/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"Vimezimwa"</item> <item msgid="3028994095749238254">"Vimewashwa"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-sw600dp-land/dimens.xml b/packages/SystemUI/res/values-sw600dp-land/dimens.xml index 2a27b47e54ca..4a53df9c2f29 100644 --- a/packages/SystemUI/res/values-sw600dp-land/dimens.xml +++ b/packages/SystemUI/res/values-sw600dp-land/dimens.xml @@ -24,7 +24,6 @@ <!-- margin from keyguard status bar to clock. For split shade it should be keyguard_split_shade_top_margin - status_bar_header_height_keyguard = 8dp --> <dimen name="keyguard_clock_top_margin">8dp</dimen> - <dimen name="keyguard_smartspace_top_offset">0dp</dimen> <!-- QS--> <dimen name="qs_panel_padding_top">16dp</dimen> diff --git a/packages/SystemUI/res/values-sw600dp-port/config.xml b/packages/SystemUI/res/values-sw600dp-port/config.xml index f556b97eefc2..53d921b5e534 100644 --- a/packages/SystemUI/res/values-sw600dp-port/config.xml +++ b/packages/SystemUI/res/values-sw600dp-port/config.xml @@ -33,6 +33,9 @@ <!-- The number of columns in the infinite grid QuickSettings --> <integer name="quick_settings_infinite_grid_num_columns">6</integer> + <!-- The maximum width of large tiles in the infinite grid QuickSettings --> + <integer name="quick_settings_infinite_grid_tile_max_width">3</integer> + <integer name="power_menu_lite_max_columns">2</integer> <integer name="power_menu_lite_max_rows">3</integer> diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml index 393631e3364b..26f32ef60851 100644 --- a/packages/SystemUI/res/values-sw600dp/dimens.xml +++ b/packages/SystemUI/res/values-sw600dp/dimens.xml @@ -126,6 +126,4 @@ <dimen name="controls_content_padding">24dp</dimen> <dimen name="control_list_vertical_spacing">8dp</dimen> <dimen name="control_list_horizontal_spacing">16dp</dimen> - <!-- For portrait direction in unfold foldable device, we don't need keyguard_smartspace_top_offset--> - <dimen name="keyguard_smartspace_top_offset">0dp</dimen> </resources> diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml index dd4f367f9d1c..639bc394972a 100644 --- a/packages/SystemUI/res/values-ta/strings.xml +++ b/packages/SystemUI/res/values-ta/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"உள்ளீடு"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"செவித்துணைக் கருவி"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"ஆன் செய்கிறது…"</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"தானாகச் சுழற்று"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"திரையைத் தானாகச் சுழற்று"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"இருப்பிடம்"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"முன்னமைவைப் புதுப்பிக்க முடியவில்லை"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"முன்னமைவு"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"உடனடி வசனம்"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"சாதனத்தின் மைக்ரோஃபோனுக்கான தடுப்பை நீக்கவா?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"சாதனத்தின் கேமராவுக்கான தடுப்பை நீக்கவா?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"சாதனத்தின் கேமராவுக்கும் மைக்ரோஃபோனுக்குமான தடுப்பை நீக்கவா?"</string> @@ -589,8 +593,7 @@ <string name="media_projection_action_text" msgid="3634906766918186440">"இப்போது தொடங்கு"</string> <string name="empty_shade_text" msgid="8935967157319717412">"அறிவிப்புகள் இல்லை"</string> <string name="no_unseen_notif_text" msgid="395512586119868682">"புதிய அறிவிப்புகள் இல்லை"</string> - <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) --> - <skip /> + <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"குறைந்த ஒலியளவில் அறிவிப்புகள் இப்போது இயக்கப்பட்டுள்ளது"</string> <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"ஒரே நேரம் பல அறிவிப்புகள் வரும்போது சாதன ஒலியளவும் விழிப்பூட்டலும் தானாக 2 நிமிடம் குறைக்கப்படும்."</string> <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"முடக்கு"</string> <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"பழைய அறிவிப்பைப் பார்க்க அன்லாக் செய்க"</string> @@ -698,8 +701,7 @@ <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> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"ரிங்கர் பயன்முறை"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"ஒலியடக்கும்"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ஒலி இயக்கும்"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"அதிர்வுறும்"</string> @@ -869,6 +871,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"திரைப் பிரிப்பைப் பயன்படுத்தும்போது வலது/கீழ் உள்ள ஆப்ஸுக்கு மாறுதல்"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"திரைப் பிரிப்பைப் பயன்படுத்தும்போது இடது/மேலே உள்ள ஆப்ஸுக்கு மாறுதல்"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"திரைப் பிரிப்பின்போது: ஓர் ஆப்ஸுக்குப் பதிலாக மற்றொன்றை மாற்றுதல்"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"உள்ளீடு"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"அடுத்த மொழிக்கு மாற்றுதல்"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"முந்தைய மொழிக்கு மாற்றுதல்"</string> @@ -1411,15 +1415,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"பிரத்தியேகப்படுத்தும்"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"முடிந்தது"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"விரிவாக்குவதற்கான ஐகான்"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"அல்லது"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"இழுப்பதற்கான ஹேண்டில்"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"கீபோர்டு அமைப்புகள்"</string> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <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-ta/tiles_states_strings.xml b/packages/SystemUI/res/values-ta/tiles_states_strings.xml index a3b9538f891c..66cdeec01991 100644 --- a/packages/SystemUI/res/values-ta/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-ta/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"முடக்கப்பட்டுள்ளது"</item> <item msgid="3028994095749238254">"இயக்கப்பட்டுள்ளது"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml index ed4c0eff770f..2834196437d5 100644 --- a/packages/SystemUI/res/values-te/strings.xml +++ b/packages/SystemUI/res/values-te/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"ఇన్పుట్"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"వినికిడి పరికరాలు"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"ఆన్ చేస్తోంది…"</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ఆటో-రొటేట్"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"స్క్రీన్ ఆటో-రొటేట్"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"లొకేషన్"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"ప్రీసెట్ను అప్డేట్ చేయడం సాధ్యపడలేదు"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"ప్రీసెట్"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"లైవ్ క్యాప్షన్"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"పరికరం మైక్రోఫోన్ను అన్బ్లాక్ చేయమంటారా?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"పరికరంలోని కెమెరాను అన్బ్లాక్ చేయమంటారా?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"పరికరంలోని కెమెరా, మైక్రోఫోన్లను అన్బ్లాక్ చేయమంటారా?"</string> @@ -589,8 +593,7 @@ <string name="media_projection_action_text" msgid="3634906766918186440">"ఇప్పుడే ప్రారంభించండి"</string> <string name="empty_shade_text" msgid="8935967157319717412">"నోటిఫికేషన్లు లేవు"</string> <string name="no_unseen_notif_text" msgid="395512586119868682">"కొత్త నోటిఫికేషన్లు ఏవీ లేవు"</string> - <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) --> - <skip /> + <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"నోటిఫికేషన్ కూల్డౌన్ ఇప్పుడు ఆన్ అయ్యింది"</string> <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"ఒకేసారి పలు నోటిఫికేషన్లు వస్తే, పరికర వాల్యూమ్, అలర్ట్స్ ఆటోమేటిగ్గా 2 నిమిషాలకు తగ్గించబడతాయి."</string> <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"ఆఫ్ చేయండి"</string> <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"పాత నోటిఫికేషన్ల కోసం అన్లాక్ చేయండి"</string> @@ -698,8 +701,7 @@ <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> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"రింగర్ మోడ్"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"మ్యూట్ చేయి"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"అన్మ్యూట్ చేయి"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"వైబ్రేట్"</string> @@ -869,6 +871,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"స్ప్లిట్ స్క్రీన్ ఉపయోగిస్తున్నప్పుడు కుడి లేదా కింద యాప్నకు మారండి"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"స్ప్లిట్ స్క్రీన్ ఉపయోగిస్తున్నప్పుడు ఎడమ లేదా పైన యాప్నకు మారండి"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"స్ప్లిట్ స్క్రీన్ సమయంలో: ఒక దాన్నుండి మరో దానికి యాప్ రీప్లేస్ చేయండి"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"ఇన్పుట్"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"తర్వాత భాషకు స్విచ్ అవ్వండి"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"మునుపటి భాషకు స్విచ్ అవ్వండి"</string> @@ -1411,15 +1415,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"అనుకూలంగా మార్చండి"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"పూర్తయింది"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"విస్తరించండి చిహ్నం"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"లేదా"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"లాగే హ్యాండిల్"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"కీబోర్డ్ సెట్టింగ్లు"</string> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <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/tiles_states_strings.xml b/packages/SystemUI/res/values-te/tiles_states_strings.xml index 6584cdd59282..42ee13d8cc57 100644 --- a/packages/SystemUI/res/values-te/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-te/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"ఆఫ్లో ఉంది"</item> <item msgid="3028994095749238254">"ఆన్లో ఉంది"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml index 49ba43ee149f..fcfc4e2364e0 100644 --- a/packages/SystemUI/res/values-th/strings.xml +++ b/packages/SystemUI/res/values-th/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"อินพุต"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"เครื่องช่วยฟัง"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"กำลังเปิด..."</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"หมุนอัตโนมัติ"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"หมุนหน้าจออัตโนมัติ"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"ตำแหน่ง"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"ไม่สามารถอัปเดตค่าที่กำหนดล่วงหน้า"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"ค่าที่กำหนดล่วงหน้า"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"คำบรรยายสด"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"เลิกบล็อกไมโครโฟนของอุปกรณ์ใช่ไหม"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"เลิกบล็อกกล้องของอุปกรณ์ใช่ไหม"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"เลิกบล็อกกล้องและไมโครโฟนของอุปกรณ์ใช่ไหม"</string> @@ -589,8 +593,7 @@ <string name="media_projection_action_text" msgid="3634906766918186440">"เริ่มเลย"</string> <string name="empty_shade_text" msgid="8935967157319717412">"ไม่มีการแจ้งเตือน"</string> <string name="no_unseen_notif_text" msgid="395512586119868682">"ไม่มีการแจ้งเตือนใหม่"</string> - <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) --> - <skip /> + <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"การพักการแจ้งเตือนเปิดอยู่"</string> <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"ระบบจะลดระดับเสียงและจำนวนการแจ้งเตือนของอุปกรณ์โดยอัตโนมัติสูงสุด 2 นาทีเมื่อคุณได้รับการแจ้งเตือนพร้อมกันมากเกินไป"</string> <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"ปิด"</string> <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"ปลดล็อกเพื่อดูการแจ้งเตือนเก่า"</string> @@ -698,8 +701,7 @@ <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> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"โหมดเสียงเรียกเข้า"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"ปิดเสียง"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"เปิดเสียง"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"สั่น"</string> @@ -869,6 +871,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"เปลี่ยนไปใช้แอปทางด้านขวาหรือด้านล่างขณะใช้โหมดแยกหน้าจอ"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"เปลี่ยนไปใช้แอปทางด้านซ้ายหรือด้านบนขณะใช้โหมดแยกหน้าจอ"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"ระหว่างใช้โหมดแยกหน้าจอ: เปลี่ยนแอปหนึ่งเป็นอีกแอปหนึ่ง"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"อินพุต"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"เปลี่ยนเป็นภาษาถัดไป"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"เปลี่ยนเป็นภาษาก่อนหน้า"</string> @@ -1411,15 +1415,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"ปรับแต่ง"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"เสร็จสิ้น"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ไอคอนขยาย"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"หรือ"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"แฮนเดิลการลาก"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"การตั้งค่าแป้นพิมพ์"</string> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <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> @@ -1433,11 +1451,11 @@ <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"ย้อนกลับ"</string> <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"ใช้ 3 นิ้วปัดไปทางซ้ายหรือขวาบนทัชแพด"</string> <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"ดีมาก"</string> - <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"คุณทำท่าทางสัมผัสเพื่อย้อนกลับเสร็จแล้ว"</string> + <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"คุณทำท่าทางสัมผัสเพื่อย้อนกลับสำเร็จแล้ว"</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"ไปที่หน้าแรก"</string> <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"ใช้ 3 นิ้วปัดขึ้นบนทัชแพด"</string> <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"เก่งมาก"</string> - <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"คุณทำท่าทางสัมผัสเพื่อไปที่หน้าแรกเสร็จแล้ว"</string> + <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"คุณทำท่าทางสัมผัสเพื่อไปที่หน้าแรกสำเร็จแล้ว"</string> <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"ดูแอปล่าสุด"</string> <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"ใช้ 3 นิ้วปัดขึ้นแล้วค้างไว้บนทัชแพด"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"เยี่ยมมาก"</string> @@ -1445,7 +1463,7 @@ <string name="tutorial_action_key_title" msgid="8172535792469008169">"ดูแอปทั้งหมด"</string> <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"กดปุ่มดำเนินการบนแป้นพิมพ์"</string> <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"ยอดเยี่ยม"</string> - <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"คุณทำท่าทางสัมผัสเพื่อดูแอปทั้งหมดเสร็จแล้ว"</string> + <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"คุณทำท่าทางสัมผัสเพื่อดูแอปทั้งหมดสำเร็จแล้ว"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"ไฟแบ็กไลต์ของแป้นพิมพ์"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"ระดับที่ %1$d จาก %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"ระบบควบคุมอุปกรณ์สมาร์ทโฮม"</string> diff --git a/packages/SystemUI/res/values-th/tiles_states_strings.xml b/packages/SystemUI/res/values-th/tiles_states_strings.xml index 8b7187b66baa..d249057be4da 100644 --- a/packages/SystemUI/res/values-th/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-th/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"ปิด"</item> <item msgid="3028994095749238254">"เปิด"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml index e589d31a4900..68cc6d2fb872 100644 --- a/packages/SystemUI/res/values-tl/strings.xml +++ b/packages/SystemUI/res/values-tl/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Input"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Mga hearing aid"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Ino-on…"</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"I-auto rotate"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Awtomatikong i-rotate ang screen"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Lokasyon"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"Hindi ma-update ang preset"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Preset"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Instant Caption"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"I-unblock ang mikropono ng device?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"I-unblock ang camera ng device?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"I-unblock ang camera at mikropono ng device?"</string> @@ -589,8 +593,7 @@ <string name="media_projection_action_text" msgid="3634906766918186440">"Magsimula ngayon"</string> <string name="empty_shade_text" msgid="8935967157319717412">"Walang mga notification"</string> <string name="no_unseen_notif_text" msgid="395512586119868682">"Walang bagong notification"</string> - <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) --> - <skip /> + <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"Naka-on na ang cooldown sa notification"</string> <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Babawasan ang volume at alerto nang hanggang 2 minuto kapag nakatanggap ng maraming notification."</string> <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"I-off"</string> <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"I-unlock para makita ang mga mas lumang notification"</string> @@ -698,8 +701,7 @@ <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Nakapirmi"</string> <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Pag-track ng Ulo"</string> <string name="volume_ringer_change" msgid="3574969197796055532">"I-tap para baguhin ang ringer mode"</string> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"ringer mode"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"i-mute"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"i-unmute"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"i-vibrate"</string> @@ -869,6 +871,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Lumipat sa app sa kanan o ibaba habang ginagamit ang split screen"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Lumipat sa app sa kaliwa o itaas habang ginagamit ang split screen"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"Habang nasa split screen: magpalit-palit ng app"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Input"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"Lumipat sa susunod na wika"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"Lumipat sa dating wika"</string> @@ -881,7 +885,7 @@ <string name="keyboard_shortcut_group_applications_email" msgid="7852376788894975192">"Email"</string> <string name="keyboard_shortcut_group_applications_sms" msgid="6912633831752843566">"SMS"</string> <string name="keyboard_shortcut_group_applications_music" msgid="9032078456666204025">"Music"</string> - <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Kalendaryo"</string> + <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Calendar"</string> <string name="keyboard_shortcut_group_applications_calculator" msgid="6316043911946540137">"Calculator"</string> <string name="keyboard_shortcut_group_applications_maps" msgid="7312554713993114342">"Mga mapa"</string> <string name="volume_and_do_not_disturb" msgid="502044092739382832">"Huwag Istorbohin"</string> @@ -1411,15 +1415,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"I-customize"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Tapos na"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"I-expand ang icon"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"o"</string> <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> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <skip /> <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-tl/tiles_states_strings.xml b/packages/SystemUI/res/values-tl/tiles_states_strings.xml index fe2827f6a4e9..0e43fafd1a04 100644 --- a/packages/SystemUI/res/values-tl/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-tl/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"Naka-off"</item> <item msgid="3028994095749238254">"Naka-on"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml index 6c2aa50a248f..fd4bb451f024 100644 --- a/packages/SystemUI/res/values-tr/strings.xml +++ b/packages/SystemUI/res/values-tr/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Giriş"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"İşitme cihazları"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Açılıyor…"</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Otomatik döndür"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Ekranı otomatik döndür"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Konum"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"Hazır ayar güncellenemedi"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Hazır Ayar"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Canlı Altyazı"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Cihaz mikrofonunun engellemesi kaldırılsın mı?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Cihaz kamerasının engellemesi kaldırılsın mı?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Cihaz kamerası ile mikrofonunun engellemesi kaldırılsın mı?"</string> @@ -698,8 +702,7 @@ <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Sabit"</string> <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Baş Takibi"</string> <string name="volume_ringer_change" msgid="3574969197796055532">"Telefon zili modunu değiştirmek için dokunun"</string> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"telefon zili modu"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"sesi kapat"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"sesi aç"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"titreşim"</string> @@ -869,6 +872,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Bölünmüş ekran kullanırken sağdaki veya alttaki uygulamaya geçiş yap"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Bölünmüş ekran kullanırken soldaki veya üstteki uygulamaya geçiş yapın"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"Bölünmüş ekran etkinken: Bir uygulamayı başkasıyla değiştir"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Giriş"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"Sonraki dile geç"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"Önceki dile geç"</string> @@ -1411,15 +1416,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Özelleştir"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Bitti"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Genişlet simgesi"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"veya"</string> <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> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <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-tr/tiles_states_strings.xml b/packages/SystemUI/res/values-tr/tiles_states_strings.xml index 1ed106f4efd2..1e30c6d3a5c1 100644 --- a/packages/SystemUI/res/values-tr/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-tr/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"Kapalı"</item> <item msgid="3028994095749238254">"Açık"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml index a7590abcf486..ab5f8a42697a 100644 --- a/packages/SystemUI/res/values-uk/strings.xml +++ b/packages/SystemUI/res/values-uk/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Джерело сигналу"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Слухові апарати"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Увімкнення…"</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Автообертання"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Автоматично обертати екран"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Геодані"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"Не вдалось оновити набір налаштувань"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Набір налаштувань"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Живі субтитри"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Надати доступ до мікрофона?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Надати доступ до камери пристрою?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Надати доступ до камери й мікрофона?"</string> @@ -698,8 +702,7 @@ <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> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"режим дзвінка"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"вимкнути звук"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"увімкнути звук"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"увімкнути вібросигнал"</string> @@ -869,6 +872,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Перейти до додатка праворуч або внизу на розділеному екрані"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Під час розділення екрана перемикатися на додаток ліворуч або вгорі"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"Під час розділення екрана: замінити додаток іншим"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Метод введення"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"Вибрати наступну мову"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"Вибрати попередню мову"</string> @@ -1411,21 +1416,35 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Налаштувати"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Готово"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Значок розгортання"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"або"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Маркер переміщення"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Налаштування клавіатури"</string> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <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_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-uk/tiles_states_strings.xml b/packages/SystemUI/res/values-uk/tiles_states_strings.xml index 61e62e4395ce..6c03aea7b3af 100644 --- a/packages/SystemUI/res/values-uk/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-uk/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"Вимкнено"</item> <item msgid="3028994095749238254">"Увімкнено"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml index f377ecd93d64..39aa5d1ccfcc 100644 --- a/packages/SystemUI/res/values-ur/strings.xml +++ b/packages/SystemUI/res/values-ur/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"ان پٹ"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"سماعتی آلات"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"آن ہو رہا ہے…"</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"خود کار طور پر گھمائیں"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"اسکرین کو خود کار طور پر گھمائیں"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"مقام"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"پہلے سے ترتیب شدہ کو اپ ڈیٹ نہیں کیا جا سکا"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"پہلے سے ترتیب شدہ"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"لائیو کیپشن"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"آلے کا مائیکروفون غیر مسدود کریں؟"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"آلے کا کیمرا غیر مسدود کریں؟"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"آلے کا کیمرا اور مائیکروفون غیر مسدود کریں؟"</string> @@ -698,8 +702,7 @@ <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> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"رنگر موڈ"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"خاموش کریں"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"غیر خاموش کریں"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"وائبریٹ"</string> @@ -869,6 +872,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"اسپلٹ اسکرین کا استعمال کرتے ہوئے دائیں یا نیچے ایپ پر سوئچ کریں"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"اسپلٹ اسکرین کا استعمال کرتے ہوئے بائیں یا اوپر ایپ پر سوئچ کریں"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"اسپلٹ اسکرین کے دوران: ایک ایپ کو دوسرے سے تبدیل کریں"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"ان پٹ"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"اگلی زبان پر سوئچ کریں"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"پچھلی زبان پر سوئچ کریں"</string> @@ -1411,15 +1416,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"حسب ضرورت بنائیں"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"ہو گیا"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"آئیکن پھیلائیں"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"یا"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"گھسیٹنے کا ہینڈل"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"کی بورڈ کی ترتیبات"</string> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <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> @@ -1441,11 +1460,11 @@ <string name="touchpad_recent_apps_gesture_action_title" msgid="934906836867137906">"حالیہ ایپس دیکھیں"</string> <string name="touchpad_recent_apps_gesture_guidance" msgid="6304446013842271822">"اپنے ٹچ پیڈ پر تین انگلیوں کا استعمال کرتے ہوئے اوپر کی طرف سوائپ کریں اور دبائے رکھیں"</string> <string name="touchpad_recent_apps_gesture_success_title" msgid="8481920554139332593">"بہترین!"</string> - <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"آپ نے حالیہ ایپس کا اشارہ مکمل کر لیا ہے۔"</string> + <string name="touchpad_recent_apps_gesture_success_body" msgid="4334263906697493273">"آپ نے حالیہ ایپس دیکھیں کا اشارہ مکمل کر لیا ہے۔"</string> <string name="tutorial_action_key_title" msgid="8172535792469008169">"سبھی ایپس دیکھیں"</string> <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"اپنے کی بورڈ پر ایکشن کلید دبائیں"</string> <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"بہت خوب!"</string> - <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"آپ نے سبھی ایپس کا اشارہ مکمل کر لیا ہے"</string> + <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"آپ نے سبھی ایپس دیکھیں کا اشارہ مکمل کر لیا ہے"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"کی بورڈ بیک لائٹ"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d میں سے %1$d کا لیول"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"ہوم کنٹرولز"</string> diff --git a/packages/SystemUI/res/values-ur/tiles_states_strings.xml b/packages/SystemUI/res/values-ur/tiles_states_strings.xml index ebbc30ebca58..a213f00e496d 100644 --- a/packages/SystemUI/res/values-ur/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-ur/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"آف ہے"</item> <item msgid="3028994095749238254">"آن ہے"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml index f8b944a672ce..67cc8b638a92 100644 --- a/packages/SystemUI/res/values-uz/strings.xml +++ b/packages/SystemUI/res/values-uz/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Kirish"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Eshitish moslamalari"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Yoqilmoqda…"</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Avto-burilish"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Ekranning avtomatik burilishi"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Joylashuv"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"Andoza yangilanmadi"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Andoza"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Jonli izoh"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Qurilma mikrofoni blokdan chiqarilsinmi?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Qurilma kamerasi blokdan chiqarilsinmi?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Qurilma kamerasi va mikrofoni blokdan chiqarilsinmi?"</string> @@ -589,8 +593,7 @@ <string name="media_projection_action_text" msgid="3634906766918186440">"Boshlash"</string> <string name="empty_shade_text" msgid="8935967157319717412">"Bildirishnomalar yo‘q"</string> <string name="no_unseen_notif_text" msgid="395512586119868682">"Yangi bildirishoma yoʻq"</string> - <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) --> - <skip /> + <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"Bildirishnomalarni sekinlatish yoqildi"</string> <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"Bir vaqtda juda koʻp bildirishnoma olsangiz, qurilmangiz tovushi va ogohlantirishlar 2 daqiqagacha avtomatik pasaytiriladi."</string> <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"Faolsizlantirish"</string> <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"Eskilarini koʻrish uchun qulfni yeching"</string> @@ -698,8 +701,7 @@ <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Statik"</string> <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Boshni kuzatish"</string> <string name="volume_ringer_change" msgid="3574969197796055532">"Jiringlagich rejimini oʻzgartirish uchun bosing"</string> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"jiringlagich rejimi"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"ovozsiz qilish"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ovozni yoqish"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"tebranish"</string> @@ -869,6 +871,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Ajratilgan ekranda oʻngdagi yoki pastdagi ilovaga almashish"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Ajratilgan ekranda chapdagi yoki yuqoridagi ilovaga almashish"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"Ajratilgan rejimda ilovalarni oʻzaro almashtirish"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Kiritish"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"Keyingi tilga almashtirish"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"Avvalgi tilga almashtirish"</string> @@ -1078,7 +1082,7 @@ <string name="accessibility_magnification_zoom" msgid="4222088982642063979">"Masshtab"</string> <string name="accessibility_magnification_medium" msgid="6994632616884562625">"Oʻrtacha"</string> <string name="accessibility_magnification_small" msgid="8144502090651099970">"Kichik"</string> - <string name="accessibility_magnification_large" msgid="6602944330021308774">"Yirik"</string> + <string name="accessibility_magnification_large" msgid="6602944330021308774">"Katta"</string> <string name="accessibility_magnification_fullscreen" msgid="5043514702759201964">"Butun ekran"</string> <string name="accessibility_magnification_done" msgid="263349129937348512">"Tayyor"</string> <string name="accessibility_magnifier_edit" msgid="1522877239671820636">"Tahrirlash"</string> @@ -1411,15 +1415,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Moslash"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Tayyor"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Yoyish belgisi"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"yoki"</string> <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> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <skip /> <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> @@ -1427,14 +1445,14 @@ <string name="launch_keyboard_touchpad_tutorial_notification_title" msgid="1940023776496198762">"Klaviatura va sensorli panel yordamida kezing"</string> <string name="launch_keyboard_touchpad_tutorial_notification_content" msgid="1780725168171929365">"Sensorli panel ishoralari, tezkor tugmalar va boshqalar haqida"</string> <string name="touchpad_tutorial_back_gesture_button" msgid="3104716365403620315">"Orqaga"</string> - <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Boshiga"</string> + <string name="touchpad_tutorial_home_gesture_button" msgid="8023973153559885624">"Boshiga qaytish"</string> <string name="touchpad_tutorial_recent_apps_gesture_button" msgid="8919227647650347359">"Oxirgi ilovalarni koʻrish"</string> <string name="touchpad_tutorial_done_button" msgid="176168488821755503">"Tayyor"</string> <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"Orqaga qaytish"</string> <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"Sensorli panelda uchta barmoq bilan chapga yoki oʻngga suring"</string> <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"Yaxshi!"</string> <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"Ortga qaytish ishorasi darsini tamomladingiz."</string> - <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Boshiga"</string> + <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"Boshiga qaytish"</string> <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"Sensorli panelda uchta barmoq bilan tepaga suring"</string> <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"Barakalla!"</string> <string name="touchpad_home_gesture_success_body" msgid="2590690589194027059">"Bosh ekranni ochish ishorasi darsini tamomladingiz"</string> diff --git a/packages/SystemUI/res/values-uz/tiles_states_strings.xml b/packages/SystemUI/res/values-uz/tiles_states_strings.xml index 2ae811233176..5e6611c3c1d5 100644 --- a/packages/SystemUI/res/values-uz/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-uz/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"Oʻchiq"</item> <item msgid="3028994095749238254">"Yoniq"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml index bdcea11fa79a..6752ceb27e97 100644 --- a/packages/SystemUI/res/values-vi/strings.xml +++ b/packages/SystemUI/res/values-vi/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Thiết bị đầu vào"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Thiết bị trợ thính"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Đang bật…"</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Tự động xoay"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Tự động xoay màn hình"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Vị trí"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"Không cập nhật được giá trị đặt trước"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Chế độ đặt sẵn"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Phụ đề trực tiếp"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Bỏ chặn micrô của thiết bị?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Bỏ chặn camera của thiết bị?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Bỏ chặn máy ảnh và micrô của thiết bị?"</string> @@ -698,8 +702,7 @@ <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Cố định"</string> <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Theo dõi chuyển động của đầu"</string> <string name="volume_ringer_change" msgid="3574969197796055532">"Nhấn để thay đổi chế độ chuông"</string> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"chế độ chuông"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"tắt tiếng"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"bật tiếng"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"rung"</string> @@ -869,6 +872,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Chuyển sang ứng dụng bên phải hoặc ở dưới khi đang chia đôi màn hình"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Chuyển sang ứng dụng bên trái hoặc ở trên khi đang chia đôi màn hình"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"Trong chế độ chia đôi màn hình: thay một ứng dụng bằng ứng dụng khác"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Đầu vào"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"Chuyển sang ngôn ngữ tiếp theo"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"Chuyển về ngôn ngữ trước"</string> @@ -1411,15 +1416,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Tuỳ chỉnh"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Xong"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Biểu tượng Mở rộng"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"hoặc"</string> <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> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <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-vi/tiles_states_strings.xml b/packages/SystemUI/res/values-vi/tiles_states_strings.xml index d9d8af1d644c..8aa360bbeaf7 100644 --- a/packages/SystemUI/res/values-vi/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-vi/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"Đang tắt"</item> <item msgid="3028994095749238254">"Đang bật"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml index 21015392b6d0..6e76bb7dbe4e 100644 --- a/packages/SystemUI/res/values-zh-rCN/strings.xml +++ b/packages/SystemUI/res/values-zh-rCN/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"输入"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"助听器"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"正在开启…"</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"自动屏幕旋转"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"自动旋转屏幕"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"位置信息"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"无法更新预设"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"预设"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"实时字幕"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"要解锁设备麦克风吗?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"要解锁设备摄像头吗?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"要解锁设备摄像头和麦克风吗?"</string> @@ -589,8 +593,7 @@ <string name="media_projection_action_text" msgid="3634906766918186440">"立即开始"</string> <string name="empty_shade_text" msgid="8935967157319717412">"没有通知"</string> <string name="no_unseen_notif_text" msgid="395512586119868682">"没有新通知"</string> - <!-- no translation found for adaptive_notification_edu_hun_title (2594042455998795122) --> - <skip /> + <string name="adaptive_notification_edu_hun_title" msgid="2594042455998795122">"“通知音量渐降”功能现已开启"</string> <string name="adaptive_notification_edu_hun_text" msgid="7743367744129536610">"如果您在短时间内收到很多通知,设备音量和提醒次数会自动降低,最长持续 2 分钟。"</string> <string name="go_to_adaptive_notification_settings" msgid="2423690125178298479">"关闭"</string> <string name="unlock_to_see_notif_text" msgid="7439033907167561227">"解锁即可查看旧通知"</string> @@ -698,8 +701,7 @@ <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> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"响铃模式"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"静音"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"取消静音"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"振动"</string> @@ -869,6 +871,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"使用分屏模式时,切换到右侧或下方的应用"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"使用分屏模式时,切换到左侧或上方的应用"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"在分屏期间:将一个应用替换为另一个应用"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"输入"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"切换到下一种语言"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"切换到上一种语言"</string> @@ -1411,15 +1415,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"自定义"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"完成"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"展开图标"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"或"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"拖动手柄"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"键盘设置"</string> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <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> @@ -1433,7 +1451,7 @@ <string name="touchpad_back_gesture_action_title" msgid="7199067250654332735">"返回"</string> <string name="touchpad_back_gesture_guidance" msgid="5352221087725906542">"在触控板上用三根手指向左或向右滑动"</string> <string name="touchpad_back_gesture_success_title" msgid="7370719098633023496">"太棒了!"</string> - <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"您完成了“返回”手势教程。"</string> + <string name="touchpad_back_gesture_success_body" msgid="2324724953720741719">"您已完成“返回”手势教程。"</string> <string name="touchpad_home_gesture_action_title" msgid="8885107349719257882">"前往主屏幕"</string> <string name="touchpad_home_gesture_guidance" msgid="4178219118381915899">"在触控板上用三根手指向上滑动"</string> <string name="touchpad_home_gesture_success_title" msgid="3648264553645798470">"太棒了!"</string> @@ -1445,7 +1463,7 @@ <string name="tutorial_action_key_title" msgid="8172535792469008169">"查看所有应用"</string> <string name="tutorial_action_key_guidance" msgid="5040613427202799294">"按键盘上的快捷操作按键"</string> <string name="tutorial_action_key_success_title" msgid="2371827347071979571">"非常棒!"</string> - <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"您已完成“查看所有应用”手势"</string> + <string name="tutorial_action_key_success_body" msgid="1688986269491357832">"您已完成“查看所有应用”手势教程"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"键盘背光"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"第 %1$d 级,共 %2$d 级"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"家居控制"</string> diff --git a/packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml b/packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml index 7748251b9430..2259076341cf 100644 --- a/packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"已关闭"</item> <item msgid="3028994095749238254">"已开启"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml index b2e7886fa6eb..32fe2a100b69 100644 --- a/packages/SystemUI/res/values-zh-rHK/strings.xml +++ b/packages/SystemUI/res/values-zh-rHK/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"輸入"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"助聽器"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"正在開啟…"</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"自動旋轉"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"自動旋轉螢幕"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"位置"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"無法更新預設"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"預設"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"即時字幕"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"要解除封鎖裝置麥克風嗎?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"要解除封鎖裝置相機嗎?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"要解除封鎖裝置相機和麥克風嗎?"</string> @@ -698,8 +702,7 @@ <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> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"響鈴模式"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"靜音"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"取消靜音"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"震動"</string> @@ -869,6 +872,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"使用分割螢幕時,切換至右邊或下方的應用程式"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"使用分割螢幕時,切換至左邊或上方的應用程式"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"使用分割螢幕期間:更換應用程式"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"輸入"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"切換至下一個語言"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"切換至上一個語言"</string> @@ -1411,15 +1416,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"自訂"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"完成"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"展開圖示"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"或"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"拖曳控點"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"鍵盤設定"</string> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <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/tiles_states_strings.xml b/packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml index cca7ac42906a..c5e05c91e5f1 100644 --- a/packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"關閉"</item> <item msgid="3028994095749238254">"開啟"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml index adb2838345a7..9778e7022725 100644 --- a/packages/SystemUI/res/values-zh-rTW/strings.xml +++ b/packages/SystemUI/res/values-zh-rTW/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"輸入"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"助聽器"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"開啟中…"</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"自動旋轉"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"自動旋轉螢幕"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"定位"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"無法更新預設設定"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"預設"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"即時字幕"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"要解除封鎖裝置麥克風嗎?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"解除封鎖裝置相機?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"要將裝置的相機和麥克風解除封鎖嗎?"</string> @@ -698,8 +702,7 @@ <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> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"鈴聲模式"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"靜音"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"取消靜音"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"震動"</string> @@ -869,6 +872,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"使用分割畫面時,切換到右邊或上方的應用程式"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"使用分割畫面時,切換到左邊或上方的應用程式"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"使用分割畫面期間:更換應用程式"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"輸入"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"切換到下一個語言"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"切換到上一個語言"</string> @@ -1411,15 +1416,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"自訂"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"完成"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"展開圖示"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"或"</string> <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"拖曳控點"</string> <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"鍵盤設定"</string> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <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/tiles_states_strings.xml b/packages/SystemUI/res/values-zh-rTW/tiles_states_strings.xml index 4cc580434a36..2d34b380af52 100644 --- a/packages/SystemUI/res/values-zh-rTW/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-zh-rTW/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"已關閉"</item> <item msgid="3028994095749238254">"已開啟"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml index 245ece888fd1..9f8cf1fe2610 100644 --- a/packages/SystemUI/res/values-zu/strings.xml +++ b/packages/SystemUI/res/values-zu/strings.xml @@ -326,6 +326,8 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Okokufaka"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Imishini yendlebe"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Iyavula..."</string> + <!-- no translation found for quick_settings_brightness_unable_adjust_msg (786478497970492300) --> + <skip /> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Ukuphenduka okuzenzakalelayo"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Phendula iskrini ngokuzenzakalela"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Indawo"</string> @@ -414,6 +416,8 @@ <string name="hearing_devices_presets_error" msgid="350363093458408536">"Ayikwazanga ukubuyekeza ukusetha ngaphambilini"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Ukusetha ngaphambilini"</string> <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Okushuthwe Bukhoma"</string> + <!-- no translation found for quick_settings_notes_label (1028004078001002623) --> + <skip /> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Vulela imakrofoni yedivayisi?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Vulela ikhamera yedivayisi?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Vulela ikhamera yedivayisi nemakrofoni?"</string> @@ -698,8 +702,7 @@ <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Okugxilile"</string> <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Ukulandelela Ikhanda"</string> <string name="volume_ringer_change" msgid="3574969197796055532">"Thepha ukuze ushintshe imodi yokukhala"</string> - <!-- no translation found for volume_ringer_mode (6867838048430807128) --> - <skip /> + <string name="volume_ringer_mode" msgid="6867838048430807128">"imodi yokukhala"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"thulisa"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"susa ukuthula"</string> <string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"dlidliza"</string> @@ -869,6 +872,8 @@ <string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Shintshela ku-app ngakwesokudla noma ngezansi ngenkathi usebenzisa uhlukanisa isikrini"</string> <string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Shintshela ku-app ngakwesokunxele noma ngaphezulu ngenkathi usebenzisa ukuhlukanisa isikrini"</string> <string name="system_multitasking_replace" msgid="7410071959803642125">"Ngesikhathi sokuhlukaniswa kwesikrini: shintsha i-app ngenye"</string> + <!-- no translation found for system_multitasking_move_to_next_display (6169737557526976997) --> + <skip /> <string name="keyboard_shortcut_group_input" msgid="6888282716546625610">"Okokufaka"</string> <string name="input_switch_input_language_next" msgid="3782155659868227855">"Shintshela olimini olulandelayo"</string> <string name="input_switch_input_language_previous" msgid="6043341362202336623">"Shintshela olimini lwangaphambili"</string> @@ -1411,15 +1416,29 @@ <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> + <!-- no translation found for shortcut_helper_customize_mode_sub_title (2479732335876820286) --> + <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> + <!-- no translation found for shortcut_helper_content_description_meta_key (3989315044342124818) --> + <skip /> + <!-- no translation found for shortcut_helper_content_description_plus_icon (6152683734278299020) --> + <skip /> <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Enza ngendlela oyifisayo"</string> <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Kwenziwe"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Nweba isithonjana"</string> <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"noma"</string> <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> + <!-- no translation found for shortcut_helper_customize_dialog_set_shortcut_button_label (4754492225010429382) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_cancel_button_label (5595546460431741178) --> + <skip /> + <!-- no translation found for shortcut_helper_add_shortcut_dialog_placeholder (9154297849458741995) --> + <skip /> + <!-- no translation found for shortcut_helper_customize_dialog_error_message (5954264095841845768) --> + <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-zu/tiles_states_strings.xml b/packages/SystemUI/res/values-zu/tiles_states_strings.xml index a795ee8b9d75..1a7ce57601e3 100644 --- a/packages/SystemUI/res/values-zu/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-zu/tiles_states_strings.xml @@ -191,4 +191,7 @@ <item msgid="3079622119444911877">"Kuvaliwe"</item> <item msgid="3028994095749238254">"Kuvuliwe"</item> </string-array> + <!-- no translation found for tile_states_notes:0 (5894333929299989301) --> + <!-- no translation found for tile_states_notes:1 (6419996398343291862) --> + <!-- no translation found for tile_states_notes:2 (5908720590832378783) --> </resources> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 82c8c44f1efe..48af82ad7943 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -79,6 +79,9 @@ <!-- The number of columns in the infinite grid QuickSettings --> <integer name="quick_settings_infinite_grid_num_columns">4</integer> + <!-- The maximum width of large tiles in the infinite grid QuickSettings --> + <integer name="quick_settings_infinite_grid_tile_max_width">4</integer> + <!-- The number of columns in the Dual Shade QuickSettings --> <integer name="quick_settings_dual_shade_num_columns">4</integer> @@ -1086,4 +1089,9 @@ enable the desktop specific features. --> <bool name="config_enableDesktopFeatureSet">false</bool> + + <!-- + Whether the user switching can only happen by logging out and going through the system user (login screen). + --> + <bool name="config_userSwitchingMustGoThroughLoginScreen">false</bool> </resources> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 7fa287944956..67eb5b0fdf6b 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -815,8 +815,7 @@ <dimen name="keyguard_clock_top_margin">18dp</dimen> <!-- The amount to shift the clocks during a small/large transition --> <dimen name="keyguard_clock_switch_y_shift">14dp</dimen> - <!-- When large clock is showing, offset the smartspace by this amount --> - <dimen name="keyguard_smartspace_top_offset">12dp</dimen> + <!-- The amount to translate lockscreen elements on the GONE->AOD transition --> <dimen name="keyguard_enter_from_top_translation_y">-100dp</dimen> <!-- The amount to translate lockscreen elements on the GONE->AOD transition, on device fold --> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index 7d071cd4a04c..c69b98c8c37f 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -557,6 +557,16 @@ <item name="android:showWhenLocked">true</item> </style> + <style name="Theme.SystemUI.Dialog.Volume"> + <item name="android:backgroundDimEnabled">false</item> + <item name="android:showWhenLocked">true</item> + <item name="android:windowBackground">@color/transparent</item> + <item name="android:windowContentOverlay">@null</item> + <item name="android:windowFullscreen">true</item> + <item name="android:windowIsFloating">false</item> + <item name="android:windowNoTitle">true</item> + </style> + <style name="SystemUI.Material3.Slider.Volume"> <item name="trackHeight">40dp</item> <item name="thumbHeight">52dp</item> diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt index 24d619119983..df9f7053c3f3 100644 --- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt +++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt @@ -35,6 +35,7 @@ import android.view.ViewTreeObserver.OnGlobalLayoutListener import androidx.annotation.VisibleForTesting import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.customization.R import com.android.systemui.dagger.qualifiers.Background @@ -62,6 +63,7 @@ import com.android.systemui.plugins.clocks.WeatherData import com.android.systemui.plugins.clocks.ZenData import com.android.systemui.plugins.clocks.ZenData.ZenMode import com.android.systemui.res.R as SysuiR +import com.android.systemui.settings.UserTracker import com.android.systemui.shared.regionsampling.RegionSampler import com.android.systemui.statusbar.policy.BatteryController import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback @@ -80,7 +82,6 @@ import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.merge -import com.android.app.tracing.coroutines.launchTraced as launch /** * Controller for a Clock provided by the registry and used on the keyguard. Instantiated by @@ -103,6 +104,7 @@ constructor( private val featureFlags: FeatureFlagsClassic, private val zenModeController: ZenModeController, private val zenModeInteractor: ZenModeInteractor, + private val userTracker: UserTracker, ) { var loggers = listOf( @@ -120,6 +122,10 @@ constructor( connectClock(value) } + private fun is24HourFormat(userId: Int? = null): Boolean { + return DateFormat.is24HourFormat(context, userId ?: userTracker.userId) + } + private fun disconnectClock(clock: ClockController?) { if (clock == null) { return @@ -186,7 +192,7 @@ constructor( var pastVisibility: Int? = null override fun onViewAttachedToWindow(view: View) { - clock.events.onTimeFormatChanged(DateFormat.is24HourFormat(context)) + clock.events.onTimeFormatChanged(is24HourFormat()) // Match the asing for view.parent's layout classes. smallClockFrame = (view.parent as ViewGroup)?.also { frame -> @@ -218,7 +224,7 @@ constructor( largeClockOnAttachStateChangeListener = object : OnAttachStateChangeListener { override fun onViewAttachedToWindow(p0: View) { - clock.events.onTimeFormatChanged(DateFormat.is24HourFormat(context)) + clock.events.onTimeFormatChanged(is24HourFormat()) } override fun onViewDetachedFromWindow(p0: View) {} @@ -358,7 +364,7 @@ constructor( } override fun onTimeFormatChanged(timeFormat: String?) { - clock?.run { events.onTimeFormatChanged(DateFormat.is24HourFormat(context)) } + clock?.run { events.onTimeFormatChanged(is24HourFormat()) } } override fun onTimeZoneChanged(timeZone: TimeZone) { @@ -366,7 +372,7 @@ constructor( } override fun onUserSwitchComplete(userId: Int) { - clock?.run { events.onTimeFormatChanged(DateFormat.is24HourFormat(context)) } + clock?.run { events.onTimeFormatChanged(is24HourFormat(userId)) } zenModeCallback.onNextAlarmChanged() } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java index 11dde6aa0dfb..71d4e9af6f55 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java @@ -147,7 +147,7 @@ public class KeyguardClockSwitch extends RelativeLayout { mClockSwitchYAmount = mContext.getResources().getDimensionPixelSize( R.dimen.keyguard_clock_switch_y_shift); mSmartspaceTopOffset = (int) (mContext.getResources().getDimensionPixelSize( - R.dimen.keyguard_smartspace_top_offset) + com.android.systemui.customization.R.dimen.keyguard_smartspace_top_offset) * mContext.getResources().getConfiguration().fontScale / mContext.getResources().getDisplayMetrics().density * SMARTSPACE_TOP_PADDING_MULTIPLIER); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java index 1342dd05d7f2..95830b5f4ed7 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java @@ -15,6 +15,8 @@ */ package com.android.keyguard; +import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow; + import android.annotation.NonNull; import android.app.Presentation; import android.content.Context; @@ -36,18 +38,24 @@ import android.view.WindowManager; import androidx.annotation.Nullable; import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.dagger.qualifiers.Application; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dagger.qualifiers.UiBackground; import com.android.systemui.navigationbar.NavigationBarController; import com.android.systemui.navigationbar.views.NavigationBarView; import com.android.systemui.settings.DisplayTracker; +import com.android.systemui.shade.data.repository.ShadePositionRepository; +import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround; import com.android.systemui.statusbar.policy.KeyguardStateController; import dagger.Lazy; +import kotlinx.coroutines.CoroutineScope; + import java.util.concurrent.Executor; import javax.inject.Inject; +import javax.inject.Provider; @SysUISingleton public class KeyguardDisplayManager { @@ -58,6 +66,7 @@ public class KeyguardDisplayManager { private final DisplayManager mDisplayService; private final DisplayTracker mDisplayTracker; private final Lazy<NavigationBarController> mNavigationBarControllerLazy; + private final Provider<ShadePositionRepository> mShadePositionRepositoryProvider; private final ConnectedDisplayKeyguardPresentation.Factory mConnectedDisplayKeyguardPresentationFactory; private final Context mContext; @@ -102,9 +111,12 @@ public class KeyguardDisplayManager { DeviceStateHelper deviceStateHelper, KeyguardStateController keyguardStateController, ConnectedDisplayKeyguardPresentation.Factory - connectedDisplayKeyguardPresentationFactory) { + connectedDisplayKeyguardPresentationFactory, + Provider<ShadePositionRepository> shadePositionRepositoryProvider, + @Application CoroutineScope appScope) { mContext = context; mNavigationBarControllerLazy = navigationBarControllerLazy; + mShadePositionRepositoryProvider = shadePositionRepositoryProvider; uiBgExecutor.execute(() -> mMediaRouter = mContext.getSystemService(MediaRouter.class)); mDisplayService = mContext.getSystemService(DisplayManager.class); mDisplayTracker = displayTracker; @@ -112,6 +124,17 @@ public class KeyguardDisplayManager { mDeviceStateHelper = deviceStateHelper; mKeyguardStateController = keyguardStateController; mConnectedDisplayKeyguardPresentationFactory = connectedDisplayKeyguardPresentationFactory; + if (ShadeWindowGoesAround.isEnabled()) { + collectFlow(appScope, shadePositionRepositoryProvider.get().getDisplayId(), + (id) -> onShadeWindowMovedToDisplayId(id)); + } + } + + private void onShadeWindowMovedToDisplayId(int shadeDisplayId) { + if (mShowing) { + hidePresentation(shadeDisplayId); + updateDisplays(/* showing= */ true); + } } private boolean isKeyguardShowable(Display display) { @@ -119,9 +142,20 @@ public class KeyguardDisplayManager { if (DEBUG) Log.i(TAG, "Cannot show Keyguard on null display"); return false; } - if (display.getDisplayId() == mDisplayTracker.getDefaultDisplayId()) { - if (DEBUG) Log.i(TAG, "Do not show KeyguardPresentation on the default display"); - return false; + if (ShadeWindowGoesAround.isEnabled()) { + int shadeDisplayId = mShadePositionRepositoryProvider.get().getDisplayId().getValue(); + if (display.getDisplayId() == shadeDisplayId) { + if (DEBUG) { + Log.i(TAG, + "Do not show KeyguardPresentation on the shade window display"); + } + return false; + } + } else { + if (display.getDisplayId() == mDisplayTracker.getDefaultDisplayId()) { + if (DEBUG) Log.i(TAG, "Do not show KeyguardPresentation on the default display"); + return false; + } } display.getDisplayInfo(mTmpDisplayInfo); if ((mTmpDisplayInfo.flags & Display.FLAG_PRIVATE) != 0) { diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java index d6648a33a196..fc42045c02c8 100644 --- a/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java +++ b/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java @@ -62,7 +62,8 @@ public abstract class ClockRegistryModule { scope, mainDispatcher, bgDispatcher, - featureFlags.isEnabled(Flags.LOCKSCREEN_CUSTOM_CLOCKS), + com.android.systemui.Flags.lockscreenCustomClocks() + || featureFlags.isEnabled(Flags.LOCKSCREEN_CUSTOM_CLOCKS), /* handleAllUsers= */ true, new DefaultClockProvider( context, diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java index 811b47d57c1d..a46b236d46fb 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java @@ -16,6 +16,7 @@ package com.android.systemui; +import android.animation.Animator; import android.annotation.SuppressLint; import android.app.ActivityThread; import android.app.Application; @@ -135,6 +136,9 @@ public class SystemUIApplication extends Application implements if (Flags.enableLayoutTracing()) { View.setTraceLayoutSteps(true); } + if (com.android.window.flags.Flags.systemUiPostAnimationEnd()) { + Animator.setPostNotifyEndListenerEnabled(true); + } if (mProcessWrapper.isSystemUser()) { IntentFilter bootCompletedFilter = new diff --git a/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt b/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt index c9c4fd594adc..6635d8b06a5d 100644 --- a/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt @@ -22,6 +22,7 @@ import android.annotation.UserIdInt import android.app.admin.DevicePolicyManager import android.content.IntentFilter import android.os.UserHandle +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.internal.widget.LockPatternUtils import com.android.internal.widget.LockscreenCredential import com.android.keyguard.KeyguardSecurityModel @@ -57,7 +58,6 @@ import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onStart -import com.android.app.tracing.coroutines.launchTraced as launch import kotlinx.coroutines.withContext /** Defines interface for classes that can access authentication-related application state. */ @@ -178,6 +178,16 @@ interface AuthenticationRepository { * profile of an organization-owned device. */ @UserIdInt suspend fun getProfileWithMinFailedUnlockAttemptsForWipe(): Int + + /** + * Returns the device policy enforced maximum time to lock the device, in milliseconds. When the + * device goes to sleep, this is the maximum time the device policy allows to wait before + * locking the device, despite what the user setting might be set to. + */ + suspend fun getMaximumTimeToLock(): Long + + /** Returns `true` if the power button should instantly lock the device, `false` otherwise. */ + suspend fun getPowerButtonInstantlyLocks(): Boolean } @SysUISingleton @@ -324,6 +334,19 @@ constructor( } } + override suspend fun getMaximumTimeToLock(): Long { + return withContext(backgroundDispatcher) { + devicePolicyManager.getMaximumTimeToLock(/* admin= */ null, selectedUserId) + } + } + + /** Returns `true` if the power button should instantly lock the device, `false` otherwise. */ + override suspend fun getPowerButtonInstantlyLocks(): Boolean { + return withContext(backgroundDispatcher) { + lockPatternUtils.getPowerButtonInstantlyLocks(selectedUserId) + } + } + private val selectedUserId: Int @UserIdInt get() = userRepository.getSelectedUserInfo().id diff --git a/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt b/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt index 67579fd7f696..1c994731c393 100644 --- a/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt @@ -17,6 +17,7 @@ package com.android.systemui.authentication.domain.interactor import android.os.UserHandle +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.internal.widget.LockPatternUtils import com.android.internal.widget.LockPatternView import com.android.internal.widget.LockscreenCredential @@ -49,7 +50,6 @@ import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn -import com.android.app.tracing.coroutines.launchTraced as launch /** * Hosts application business logic related to user authentication. @@ -215,7 +215,7 @@ constructor( */ suspend fun authenticate( input: List<Any>, - tryAutoConfirm: Boolean = false + tryAutoConfirm: Boolean = false, ): AuthenticationResult { if (input.isEmpty()) { throw IllegalArgumentException("Input was empty!") @@ -254,6 +254,20 @@ constructor( return AuthenticationResult.FAILED } + /** + * Returns the device policy enforced maximum time to lock the device, in milliseconds. When the + * device goes to sleep, this is the maximum time the device policy allows to wait before + * locking the device, despite what the user setting might be set to. + */ + suspend fun getMaximumTimeToLock(): Long { + return repository.getMaximumTimeToLock() + } + + /** Returns `true` if the power button should instantly lock the device, `false` otherwise. */ + suspend fun getPowerButtonInstantlyLocks(): Boolean { + return !getAuthenticationMethod().isSecure || repository.getPowerButtonInstantlyLocks() + } + private suspend fun shouldSkipAuthenticationAttempt( authenticationMethod: AuthenticationMethodModel, isAutoConfirmAttempt: Boolean, diff --git a/packages/SystemUI/src/com/android/systemui/backup/BackupHelper.kt b/packages/SystemUI/src/com/android/systemui/backup/BackupHelper.kt index f8254592819b..a107322423bb 100644 --- a/packages/SystemUI/src/com/android/systemui/backup/BackupHelper.kt +++ b/packages/SystemUI/src/com/android/systemui/backup/BackupHelper.kt @@ -36,6 +36,7 @@ import com.android.systemui.controls.controller.AuxiliaryPersistenceWrapper import com.android.systemui.controls.controller.ControlsFavoritePersistenceWrapper import com.android.systemui.keyguard.domain.backup.KeyguardQuickAffordanceBackupHelper import com.android.systemui.people.widget.PeopleBackupHelper +import com.android.systemui.qs.panels.domain.backup.QSPreferencesBackupHelper import com.android.systemui.res.R import com.android.systemui.settings.UserFileManagerImpl @@ -58,9 +59,9 @@ open class BackupHelper : BackupAgentHelper() { private const val PEOPLE_TILES_BACKUP_KEY = "systemui.people.shared_preferences" private const val KEYGUARD_QUICK_AFFORDANCES_BACKUP_KEY = "systemui.keyguard.quickaffordance.shared_preferences" - private const val COMMUNAL_PREFS_BACKUP_KEY = - "systemui.communal.shared_preferences" + private const val COMMUNAL_PREFS_BACKUP_KEY = "systemui.communal.shared_preferences" private const val COMMUNAL_STATE_BACKUP_KEY = "systemui.communal_state" + private const val QS_PREFERENCES_BACKUP_KEY = "systemui.qs.shared_preferences" val controlsDataLock = Any() const val ACTION_RESTORE_FINISHED = "com.android.systemui.backup.RESTORE_FINISHED" const val PERMISSION_SELF = "com.android.systemui.permission.SELF" @@ -74,22 +75,20 @@ open class BackupHelper : BackupAgentHelper() { val keys = PeopleBackupHelper.getFilesToBackup() addHelper( PEOPLE_TILES_BACKUP_KEY, - PeopleBackupHelper(this, userHandle, keys.toTypedArray()) + PeopleBackupHelper(this, userHandle, keys.toTypedArray()), ) addHelper( KEYGUARD_QUICK_AFFORDANCES_BACKUP_KEY, - KeyguardQuickAffordanceBackupHelper( - context = this, - userId = userHandle.identifier, - ), + KeyguardQuickAffordanceBackupHelper(context = this, userId = userHandle.identifier), + ) + addHelper( + QS_PREFERENCES_BACKUP_KEY, + QSPreferencesBackupHelper(context = this, userId = userHandle.identifier), ) if (communalEnabled()) { addHelper( COMMUNAL_PREFS_BACKUP_KEY, - CommunalPrefsBackupHelper( - context = this, - userId = userHandle.identifier, - ) + CommunalPrefsBackupHelper(context = this, userId = userHandle.identifier), ) addHelper( COMMUNAL_STATE_BACKUP_KEY, @@ -110,10 +109,7 @@ open class BackupHelper : BackupAgentHelper() { } private fun addControlsHelper(userId: Int) { - val file = UserFileManagerImpl.createFile( - userId = userId, - fileName = CONTROLS, - ) + val file = UserFileManagerImpl.createFile(userId = userId, fileName = CONTROLS) // The map in mapOf is guaranteed to be order preserving val controlsMap = mapOf(file.getPath() to getPPControlsFile(this, userId)) NoOverwriteFileBackupHelper(controlsDataLock, this, controlsMap).also { @@ -134,6 +130,7 @@ open class BackupHelper : BackupAgentHelper() { * @property lock a lock to hold while backing up and restoring the files. * @property context the context of the [BackupAgent] * @property fileNamesAndPostProcess a map from the filenames to back up and the post processing + * * ``` * actions to take * ``` @@ -141,7 +138,7 @@ open class BackupHelper : BackupAgentHelper() { private class NoOverwriteFileBackupHelper( val lock: Any, val context: Context, - val fileNamesAndPostProcess: Map<String, () -> Unit> + val fileNamesAndPostProcess: Map<String, () -> Unit>, ) : FileBackupHelper(context, *fileNamesAndPostProcess.keys.toTypedArray()) { override fun restoreEntity(data: BackupDataInputStream) { @@ -152,11 +149,12 @@ open class BackupHelper : BackupAgentHelper() { return } synchronized(lock) { - traceSection("File restore: ${data.key}") { - super.restoreEntity(data) - } - Log.d(TAG, "Finishing restore for ${data.key} for user ${context.userId}. " + - "Starting postProcess.") + traceSection("File restore: ${data.key}") { super.restoreEntity(data) } + Log.d( + TAG, + "Finishing restore for ${data.key} for user ${context.userId}. " + + "Starting postProcess.", + ) traceSection("Postprocess: ${data.key}") { fileNamesAndPostProcess.get(data.key)?.invoke() } @@ -167,7 +165,7 @@ open class BackupHelper : BackupAgentHelper() { override fun performBackup( oldState: ParcelFileDescriptor?, data: BackupDataOutput?, - newState: ParcelFileDescriptor? + newState: ParcelFileDescriptor?, ) { synchronized(lock) { super.performBackup(oldState, data, newState) } } @@ -176,15 +174,13 @@ open class BackupHelper : BackupAgentHelper() { private fun getPPControlsFile(context: Context, userId: Int): () -> Unit { return { - val file = UserFileManagerImpl.createFile( - userId = userId, - fileName = BackupHelper.CONTROLS, - ) + val file = UserFileManagerImpl.createFile(userId = userId, fileName = BackupHelper.CONTROLS) if (file.exists()) { - val dest = UserFileManagerImpl.createFile( - userId = userId, - fileName = AuxiliaryPersistenceWrapper.AUXILIARY_FILE_NAME, - ) + val dest = + UserFileManagerImpl.createFile( + userId = userId, + fileName = AuxiliaryPersistenceWrapper.AUXILIARY_FILE_NAME, + ) file.copyTo(dest) val jobScheduler = context.getSystemService(JobScheduler::class.java) jobScheduler?.schedule( diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractor.kt index b07006887011..08b3e99fadd0 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractor.kt @@ -72,7 +72,7 @@ constructor( // Request LockSettingsService to return the Gatekeeper Password in the // VerifyCredentialResponse so that we can request a Gatekeeper HAT with the // Gatekeeper Password and operationId. - var effectiveUserId = request.userInfo.userIdForPasswordEntry + var effectiveUserId = request.userInfo.deviceCredentialOwnerId val response = if (Flags.privateSpaceBp() && effectiveUserId != request.userInfo.userId) { effectiveUserId = request.userInfo.userId diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt index cbf783d66d42..df34952f4f8d 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt @@ -34,6 +34,7 @@ import android.util.Log import android.util.RotationUtils import android.view.HapticFeedbackConstants import android.view.MotionEvent +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.keyguard.AuthInteractionProperties import com.android.launcher3.icons.IconProvider import com.android.systemui.Flags.msdlFeedback @@ -71,7 +72,6 @@ import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.update -import com.android.app.tracing.coroutines.launchTraced as launch /** ViewModel for BiometricPrompt. */ class PromptViewModel @@ -973,7 +973,7 @@ constructor( /** * The order of getting logo icon/description is: * 1. If the app sets customized icon/description, use the passed-in value - * 2. If shouldShowLogoWithOverrides(), use activityInfo to get icon/description + * 2. If shouldUseActivityLogo(), use activityInfo to get icon/description * 3. Otherwise, use applicationInfo to get icon/description */ private fun Context.getUserBadgedLogoInfo( @@ -981,6 +981,7 @@ private fun Context.getUserBadgedLogoInfo( iconProvider: IconProvider, activityTaskManager: ActivityTaskManager, ): Pair<Drawable?, String> { + // If the app sets customized icon/description, use the passed-in value directly var icon: Drawable? = if (prompt.logoBitmap != null) BitmapDrawable(resources, prompt.logoBitmap) else null var label = prompt.logoDescription ?: "" @@ -993,36 +994,28 @@ private fun Context.getUserBadgedLogoInfo( if (componentName != null && shouldUseActivityLogo(componentName)) { val activityInfo = getActivityInfo(componentName) if (activityInfo != null) { - if (icon == null) { - icon = iconProvider.getIcon(activityInfo) - } - if (label.isEmpty()) { - label = activityInfo.loadLabel(packageManager).toString() - } + icon = icon ?: iconProvider.getIcon(activityInfo) + label = label.ifEmpty { activityInfo.loadLabel(packageManager).toString() } } } - if (icon != null && label.isNotEmpty()) { - return Pair(icon, label) - } - // Use applicationInfo for other cases - val appInfo = prompt.getApplicationInfo(this, componentName) - if (appInfo == null) { - Log.w(PromptViewModel.TAG, "Cannot find app logo for package $opPackageName") - } else { - if (icon == null) { - icon = packageManager.getApplicationIcon(appInfo) - } - if (label.isEmpty()) { - label = - packageManager - .getUserBadgedLabel( - packageManager.getApplicationLabel(appInfo), - UserHandle.of(userId), - ) - .toString() + if (icon == null || label.isEmpty()) { + val appInfo = prompt.getApplicationInfo(this, componentName) + if (appInfo != null) { + icon = icon ?: packageManager.getApplicationIcon(appInfo) + label = label.ifEmpty { packageManager.getApplicationLabel(appInfo).toString() } + } else { + Log.w(PromptViewModel.TAG, "Cannot find app logo for package $opPackageName") } } + + // Add user badge + val userHandle = UserHandle.of(prompt.userInfo.userId) + if (label.isNotEmpty()) { + label = packageManager.getUserBadgedLabel(label, userHandle).toString() + } + icon = icon?.let { packageManager.getUserBadgedIcon(it, userHandle) } + return Pair(icon, label) } diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/log/CommunalMetricsLogger.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/log/CommunalMetricsLogger.kt index 7cfad60b84cd..6423f8f7783b 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/shared/log/CommunalMetricsLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/shared/log/CommunalMetricsLogger.kt @@ -30,6 +30,7 @@ constructor( @Named(LOGGABLE_PREFIXES) private val loggablePrefixes: List<String>, private val statsLogProxy: StatsLogProxy, ) { + /** Logs an add widget event for metrics. No-op if widget is not loggable. */ fun logAddWidget(componentName: String, rank: Int?) { if (!componentName.isLoggable()) { @@ -69,11 +70,21 @@ constructor( ) } + fun logResizeWidget(componentName: String, rank: Int, spanY: Int = 0) { + if (!componentName.isLoggable()) { + return + } + + statsLogProxy.writeCommunalHubWidgetEventReported( + SysUiStatsLog.COMMUNAL_HUB_WIDGET_EVENT_REPORTED__ACTION__RESIZE, + componentName, + rank = rank, + spanY = spanY, + ) + } + /** Logs loggable widgets and the total widget count as a [StatsEvent]. */ - fun logWidgetsSnapshot( - statsEvents: MutableList<StatsEvent>, - componentNames: List<String>, - ) { + fun logWidgetsSnapshot(statsEvents: MutableList<StatsEvent>, componentNames: List<String>) { val loggableComponentNames = componentNames.filter { it.isLoggable() }.toTypedArray() statsEvents.add( statsLogProxy.buildCommunalHubSnapshotStatsEvent( @@ -95,6 +106,7 @@ constructor( action: Int, componentName: String, rank: Int, + spanY: Int = 0, ) /** Builds a [SysUiStatsLog.COMMUNAL_HUB_SNAPSHOT] stats event. */ @@ -112,12 +124,14 @@ class CommunalStatsLogProxyImpl @Inject constructor() : CommunalMetricsLogger.St action: Int, componentName: String, rank: Int, + spanY: Int, ) { SysUiStatsLog.write( SysUiStatsLog.COMMUNAL_HUB_WIDGET_EVENT_REPORTED, action, componentName, rank, + spanY, ) } 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 e25ea4cbfb25..4e0e11226faa 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 @@ -178,7 +178,13 @@ abstract class BaseCommunalViewModel( * alongside the resize, in case resizing also requires re-ordering. This ensures the * re-ordering is done in the same database transaction as the resize. */ - open fun onResizeWidget(appWidgetId: Int, spanY: Int, widgetIdToRankMap: Map<Int, Int>) {} + open fun onResizeWidget( + appWidgetId: Int, + spanY: Int, + widgetIdToRankMap: Map<Int, Int>, + componentName: ComponentName, + rank: Int, + ) {} /** Called as the UI requests opening the widget editor with an optional preselected widget. */ open fun onOpenWidgetEditor(shouldOpenWidgetPickerOnStart: Boolean = false) {} 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 6508e4b574a3..ccff23003aa0 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 @@ -141,8 +141,19 @@ constructor( override fun onReorderWidgets(widgetIdToRankMap: Map<Int, Int>) = communalInteractor.updateWidgetOrder(widgetIdToRankMap) - override fun onResizeWidget(appWidgetId: Int, spanY: Int, widgetIdToRankMap: Map<Int, Int>) { + override fun onResizeWidget( + appWidgetId: Int, + spanY: Int, + widgetIdToRankMap: Map<Int, Int>, + componentName: ComponentName, + rank: Int, + ) { communalInteractor.resizeWidget(appWidgetId, spanY, widgetIdToRankMap) + metricsLogger.logResizeWidget( + componentName = componentName.flattenToString(), + rank = rank, + spanY = spanY, + ) } override fun onReorderWidgetStart() { diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt index c464a66ea0c3..6c335e71cfde 100644 --- a/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/DeviceEntryModule.kt @@ -18,13 +18,18 @@ package com.android.systemui.deviceentry import com.android.keyguard.EmptyLockIconViewController import com.android.keyguard.LockIconViewController +import com.android.systemui.CoreStartable import com.android.systemui.dagger.SysUISingleton import com.android.systemui.deviceentry.data.repository.DeviceEntryRepositoryModule import com.android.systemui.deviceentry.data.repository.FaceWakeUpTriggersConfigModule +import com.android.systemui.deviceentry.domain.interactor.DeviceUnlockedInteractor import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition +import dagger.Binds import dagger.Lazy import dagger.Module import dagger.Provides +import dagger.multibindings.ClassKey +import dagger.multibindings.IntoMap import dagger.multibindings.Multibinds @Module(includes = [DeviceEntryRepositoryModule::class, FaceWakeUpTriggersConfigModule::class]) @@ -34,6 +39,13 @@ abstract class DeviceEntryModule { */ @Multibinds abstract fun deviceEntryIconTransitionSet(): Set<DeviceEntryIconTransition> + @Binds + @IntoMap + @ClassKey(DeviceUnlockedInteractor.Activator::class) + abstract fun deviceUnlockedInteractorActivator( + activator: DeviceUnlockedInteractor.Activator + ): CoreStartable + companion object { @Provides @SysUISingleton diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepository.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepository.kt index 3f937bba46d4..675f00a89d23 100644 --- a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepository.kt @@ -5,6 +5,7 @@ import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCall import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.deviceentry.shared.model.DeviceUnlockStatus import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.user.data.repository.UserRepository import dagger.Binds @@ -42,6 +43,8 @@ interface DeviceEntryRepository { */ val isBypassEnabled: StateFlow<Boolean> + val deviceUnlockStatus: MutableStateFlow<DeviceUnlockStatus> + /** * Whether the lockscreen is enabled for the current user. This is `true` whenever the user has * chosen any secure authentication method and even if they set the lockscreen to be dismissed @@ -84,6 +87,9 @@ constructor( initialValue = keyguardBypassController.bypassEnabled, ) + override val deviceUnlockStatus = + MutableStateFlow(DeviceUnlockStatus(isUnlocked = false, deviceUnlockSource = null)) + override suspend fun isLockscreenEnabled(): Boolean { return withContext(backgroundDispatcher) { val selectedUserId = userRepository.getSelectedUserInfo().id diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractor.kt index 5259c5dca39f..b74ca035a229 100644 --- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractor.kt @@ -16,7 +16,10 @@ package com.android.systemui.deviceentry.domain.interactor +import android.provider.Settings +import android.util.Log import androidx.annotation.VisibleForTesting +import com.android.systemui.CoreStartable import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor import com.android.systemui.authentication.shared.model.AuthenticationMethodModel import com.android.systemui.dagger.SysUISingleton @@ -26,42 +29,51 @@ import com.android.systemui.deviceentry.shared.model.DeviceEntryRestrictionReaso import com.android.systemui.deviceentry.shared.model.DeviceUnlockSource import com.android.systemui.deviceentry.shared.model.DeviceUnlockStatus import com.android.systemui.flags.SystemPropertiesHelper -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.KeyguardViewMediator +import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.keyguard.domain.interactor.TrustInteractor -import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.lifecycle.ExclusiveActivatable import com.android.systemui.power.domain.interactor.PowerInteractor +import com.android.systemui.power.shared.model.WakeSleepReason +import com.android.systemui.util.settings.repository.UserAwareSecureSettingsRepository import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated import javax.inject.Inject +import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.awaitCancellation +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.distinctUntilChangedBy +import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.flatMapLatest -import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.merge -import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.launch @OptIn(ExperimentalCoroutinesApi::class) @SysUISingleton class DeviceUnlockedInteractor @Inject constructor( - @Application private val applicationScope: CoroutineScope, - authenticationInteractor: AuthenticationInteractor, - deviceEntryRepository: DeviceEntryRepository, + private val authenticationInteractor: AuthenticationInteractor, + private val repository: DeviceEntryRepository, trustInteractor: TrustInteractor, faceAuthInteractor: DeviceEntryFaceAuthInteractor, fingerprintAuthInteractor: DeviceEntryFingerprintAuthInteractor, private val powerInteractor: PowerInteractor, private val biometricSettingsInteractor: DeviceEntryBiometricSettingsInteractor, private val systemPropertiesHelper: SystemPropertiesHelper, - keyguardTransitionInteractor: KeyguardTransitionInteractor, -) { + private val userAwareSecureSettingsRepository: UserAwareSecureSettingsRepository, + private val keyguardInteractor: KeyguardInteractor, +) : ExclusiveActivatable() { private val deviceUnlockSource = merge( @@ -69,7 +81,7 @@ constructor( faceAuthInteractor.isAuthenticated .filter { it } .map { - if (deviceEntryRepository.isBypassEnabled.value) { + if (repository.isBypassEnabled.value) { DeviceUnlockSource.FaceWithBypass } else { DeviceUnlockSource.FaceWithoutBypass @@ -163,43 +175,160 @@ constructor( * proceed. */ val deviceUnlockStatus: StateFlow<DeviceUnlockStatus> = - authenticationInteractor.authenticationMethod - .flatMapLatest { authMethod -> - if (!authMethod.isSecure) { - flowOf(DeviceUnlockStatus(true, null)) - } else if (authMethod == AuthenticationMethodModel.Sim) { - // Device is locked if SIM is locked. - flowOf(DeviceUnlockStatus(false, null)) - } else { - combine( - powerInteractor.isAsleep, - isInLockdown, - keyguardTransitionInteractor - .transitionValue(KeyguardState.AOD) - .map { it == 1f } - .distinctUntilChanged(), - ::Triple, - ) - .flatMapLatestConflated { (isAsleep, isInLockdown, isAod) -> - val isForceLocked = - when { - isAsleep && !isAod -> true - isInLockdown -> true - else -> false - } - if (isForceLocked) { - flowOf(DeviceUnlockStatus(false, null)) + repository.deviceUnlockStatus.asStateFlow() + + override suspend fun onActivated(): Nothing { + authenticationInteractor.authenticationMethod.collectLatest { authMethod -> + if (!authMethod.isSecure) { + // Device remains unlocked as long as the authentication method is not secure. + Log.d(TAG, "remaining unlocked because auth method not secure") + repository.deviceUnlockStatus.value = DeviceUnlockStatus(true, null) + } else if (authMethod == AuthenticationMethodModel.Sim) { + // Device remains locked while SIM is locked. + Log.d(TAG, "remaining locked because SIM locked") + repository.deviceUnlockStatus.value = DeviceUnlockStatus(false, null) + } else { + handleLockAndUnlockEvents() + } + } + + awaitCancellation() + } + + private suspend fun handleLockAndUnlockEvents() { + try { + Log.d(TAG, "started watching for lock and unlock events") + coroutineScope { + launch { handleUnlockEvents() } + launch { handleLockEvents() } + } + } finally { + Log.d(TAG, "stopped watching for lock and unlock events") + } + } + + private suspend fun handleUnlockEvents() { + // Unlock the device when a new unlock source is detected. + deviceUnlockSource.collect { + Log.d(TAG, "unlocking due to \"$it\"") + repository.deviceUnlockStatus.value = DeviceUnlockStatus(true, it) + } + } + + private suspend fun handleLockEvents() { + merge( + // Device wakefulness events. + powerInteractor.detailedWakefulness + .map { Pair(it.isAsleep(), it.lastSleepReason) } + .distinctUntilChangedBy { it.first } + .map { (isAsleep, lastSleepReason) -> + if (isAsleep) { + if ( + lastSleepReason == WakeSleepReason.POWER_BUTTON && + authenticationInteractor.getPowerButtonInstantlyLocks() + ) { + LockImmediately("locked instantly from power button") + } else { + LockWithDelay("entering sleep") + } + } else { + CancelDelayedLock("waking up") + } + }, + // Device enters lockdown. + isInLockdown + .distinctUntilChanged() + .filter { it } + .map { LockImmediately("lockdown") }, + // Started dreaming + powerInteractor.isInteractive.flatMapLatestConflated { isInteractive -> + // Only respond to dream state changes while the device is interactive. + if (isInteractive) { + keyguardInteractor.isDreamingAny.distinctUntilChanged().map { isDreaming -> + if (isDreaming) { + LockWithDelay("started dreaming") } else { - deviceUnlockSource.map { DeviceUnlockStatus(true, it) } + CancelDelayedLock("stopped dreaming") } } + } else { + emptyFlow() + } + }, + ) + .collectLatest(::onLockEvent) + } + + private suspend fun onLockEvent(event: LockEvent) { + val debugReason = event.debugReason + when (event) { + is LockImmediately -> { + Log.d(TAG, "locking without delay due to \"$debugReason\"") + repository.deviceUnlockStatus.value = DeviceUnlockStatus(false, null) + } + + is LockWithDelay -> { + val lockDelay = lockDelay() + Log.d(TAG, "locking in ${lockDelay}ms due to \"$debugReason\"") + try { + delay(lockDelay) + Log.d( + TAG, + "locking after having waited for ${lockDelay}ms due to \"$debugReason\"", + ) + repository.deviceUnlockStatus.value = DeviceUnlockStatus(false, null) + } catch (_: CancellationException) { + Log.d( + TAG, + "delayed locking canceled, original delay was ${lockDelay}ms and reason was \"$debugReason\"", + ) } } - .stateIn( - scope = applicationScope, - started = SharingStarted.Eagerly, - initialValue = DeviceUnlockStatus(false, null), - ) + + is CancelDelayedLock -> { + // Do nothing, the mere receipt of this inside of a "latest" block means that any + // previous coroutine is automatically canceled. + } + } + } + + /** + * Returns the amount of time to wait before locking down the device after the device has been + * put to sleep by the user, in milliseconds. + */ + private suspend fun lockDelay(): Long { + val lockAfterScreenTimeoutSetting = + userAwareSecureSettingsRepository + .getInt( + Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT, + KeyguardViewMediator.KEYGUARD_LOCK_AFTER_DELAY_DEFAULT, + ) + .toLong() + Log.d(TAG, "Lock after screen timeout setting set to ${lockAfterScreenTimeoutSetting}ms") + + val maxTimeToLockDevicePolicy = authenticationInteractor.getMaximumTimeToLock() + Log.d(TAG, "Device policy max set to ${maxTimeToLockDevicePolicy}ms") + + if (maxTimeToLockDevicePolicy <= 0) { + // No device policy enforced maximum. + Log.d(TAG, "No device policy max, delay is ${lockAfterScreenTimeoutSetting}ms") + return lockAfterScreenTimeoutSetting + } + + val screenOffTimeoutSetting = + userAwareSecureSettingsRepository + .getInt( + Settings.System.SCREEN_OFF_TIMEOUT, + KeyguardViewMediator.KEYGUARD_DISPLAY_TIMEOUT_DELAY_DEFAULT, + ) + .coerceAtLeast(0) + .toLong() + Log.d(TAG, "Screen off timeout setting set to ${screenOffTimeoutSetting}ms") + + return (maxTimeToLockDevicePolicy - screenOffTimeoutSetting) + .coerceIn(minimumValue = 0, maximumValue = lockAfterScreenTimeoutSetting) + .also { Log.d(TAG, "Device policy max enforced, delay is ${it}ms") } + } private fun DeviceEntryRestrictionReason?.isInLockdown(): Boolean { return when (this) { @@ -226,7 +355,30 @@ constructor( return systemPropertiesHelper.get(SYS_BOOT_REASON_PROP) == REBOOT_MAINLINE_UPDATE } + /** [CoreStartable] that activates the [DeviceUnlockedInteractor]. */ + class Activator + @Inject + constructor( + @Application private val applicationScope: CoroutineScope, + private val interactor: DeviceUnlockedInteractor, + ) : CoreStartable { + override fun start() { + applicationScope.launch { interactor.activate() } + } + } + + private sealed interface LockEvent { + val debugReason: String + } + + private data class LockImmediately(override val debugReason: String) : LockEvent + + private data class LockWithDelay(override val debugReason: String) : LockEvent + + private data class CancelDelayedLock(override val debugReason: String) : LockEvent + companion object { + private val TAG = "DeviceUnlockedInteractor" @VisibleForTesting const val SYS_BOOT_REASON_PROP = "sys.boot.reason.last" @VisibleForTesting const val REBOOT_MAINLINE_UPDATE = "reboot,mainline_update" } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java index dd08d3262546..7a95a41770ac 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java @@ -40,7 +40,6 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.InstanceId; import com.android.internal.logging.UiEvent; import com.android.internal.logging.UiEventLogger; -import com.android.systemui.Flags; import com.android.systemui.biometrics.AuthController; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dock.DockManager; @@ -566,8 +565,7 @@ public class DozeTriggers implements DozeMachine.Part { } // When already in pulsing, we can show the new Notification without requesting a new pulse. - if (Flags.notificationPulsingFix() - && dozeState == State.DOZE_PULSING && reason == DozeLog.PULSE_REASON_NOTIFICATION) { + if (dozeState == State.DOZE_PULSING && reason == DozeLog.PULSE_REASON_NOTIFICATION) { return; } diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractor.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractor.kt index 4a369e7e849e..7758dcc0fc88 100644 --- a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractor.kt @@ -17,12 +17,14 @@ package com.android.systemui.inputdevice.tutorial.domain.interactor import android.os.SystemProperties +import com.android.internal.annotations.VisibleForTesting import com.android.systemui.dagger.SysUISingleton import com.android.systemui.inputdevice.tutorial.InputDeviceTutorialLogger import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType.KEYBOARD import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType.TOUCHPAD import com.android.systemui.inputdevice.tutorial.data.repository.TutorialSchedulerRepository +import com.android.systemui.inputdevice.tutorial.domain.interactor.TutorialSchedulerInteractor.Companion.LAUNCH_DELAY import com.android.systemui.keyboard.data.repository.KeyboardRepository import com.android.systemui.statusbar.commandline.Command import com.android.systemui.statusbar.commandline.CommandRegistry @@ -35,6 +37,7 @@ import kotlin.time.Duration.Companion.hours import kotlin.time.toKotlinDuration import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.flow @@ -96,6 +99,9 @@ constructor( private suspend fun waitForDeviceConnection(deviceType: DeviceType) = isAnyDeviceConnected[deviceType]!!.filter { it }.first() + // Only for testing notifications. This should behave independently from scheduling + @VisibleForTesting val commandTutorials = MutableStateFlow(TutorialType.NONE) + // Merging two flows ensures that tutorial is launched consecutively to avoid race condition val tutorials: Flow<TutorialType> = merge(touchpadScheduleFlow, keyboardScheduleFlow).map { @@ -146,6 +152,15 @@ constructor( pw.println("Touchpad connect time = ${repo.firstConnectionTime(TOUCHPAD)}") pw.println(" launch time = ${repo.launchTime(TOUCHPAD)}") } + "notify" -> { + if (args.size != 2) help(pw) + when (args[1]) { + "keyboard" -> commandTutorials.value = TutorialType.KEYBOARD + "touchpad" -> commandTutorials.value = TutorialType.TOUCHPAD + "both" -> commandTutorials.value = TutorialType.BOTH + else -> help(pw) + } + } else -> help(pw) } } @@ -155,6 +170,7 @@ constructor( pw.println("Available commands:") pw.println(" clear") pw.println(" info") + pw.println(" notify [keyboard|touchpad|both]") } } diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/TutorialNotificationCoordinator.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/TutorialNotificationCoordinator.kt index 3b26f2ff9deb..9dae64921d26 100644 --- a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/TutorialNotificationCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/TutorialNotificationCoordinator.kt @@ -24,6 +24,7 @@ import android.content.Context import android.content.Intent import android.os.Bundle import androidx.core.app.NotificationCompat +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background @@ -41,7 +42,7 @@ import com.android.systemui.res.R import com.android.systemui.settings.UserTracker import javax.inject.Inject import kotlinx.coroutines.CoroutineScope -import com.android.app.tracing.coroutines.launchTraced as launch +import kotlinx.coroutines.flow.merge /** When the scheduler is due, show a notification to launch tutorial */ @SysUISingleton @@ -56,7 +57,11 @@ constructor( ) { fun start() { backgroundScope.launch { - tutorialSchedulerInteractor.tutorials.collect { showNotification(it) } + merge( + tutorialSchedulerInteractor.tutorials, + tutorialSchedulerInteractor.commandTutorials, + ) + .collect { showNotification(it) } } } diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/view/KeyboardTouchpadTutorialActivity.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/view/KeyboardTouchpadTutorialActivity.kt index fa494150cbbf..fee08b31bd93 100644 --- a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/view/KeyboardTouchpadTutorialActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/ui/view/KeyboardTouchpadTutorialActivity.kt @@ -27,6 +27,7 @@ import androidx.compose.runtime.getValue import androidx.lifecycle.Lifecycle.State.STARTED import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.lifecycleScope +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.compose.theme.PlatformTheme import com.android.systemui.inputdevice.tutorial.InputDeviceTutorialLogger import com.android.systemui.inputdevice.tutorial.InputDeviceTutorialLogger.TutorialContext @@ -40,7 +41,6 @@ import com.android.systemui.inputdevice.tutorial.ui.viewmodel.Screen.BACK_GESTUR import com.android.systemui.inputdevice.tutorial.ui.viewmodel.Screen.HOME_GESTURE import java.util.Optional import javax.inject.Inject -import com.android.app.tracing.coroutines.launchTraced as launch /** * Activity for out of the box experience for keyboard and touchpad. Note that it's possible that @@ -90,6 +90,7 @@ constructor( setContent { PlatformTheme { KeyboardTouchpadTutorialContainer(vm, touchpadTutorialScreensProvider) } } + // TODO(b/376692701): Update launchTime when the activity is launched by Companion App if (savedInstanceState == null) { metricsLogger.logPeripheralTutorialLaunched( intent.getStringExtra(INTENT_TUTORIAL_ENTRY_POINT_KEY), diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/model/InternalShortcutModels.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/model/InternalShortcutModels.kt new file mode 100644 index 000000000000..8c393e27da59 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/model/InternalShortcutModels.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.keyboard.shortcut.data.model + +import android.graphics.drawable.Icon +import android.hardware.input.InputGestureData +import android.view.KeyboardShortcutGroup +import com.android.systemui.keyboard.shortcut.data.repository.ShortcutCategoriesUtils +import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategory + +/** + * Internal Keyboard Shortcut models to use with [ShortcutCategoriesUtils.fetchShortcutCategory] + * when converting API models to Shortcut Helper Model [ShortcutCategory]. These Internal Models + * bridge the Gap between [InputGestureData] from custom shortcuts API and [KeyboardShortcutGroup] + * from default shortcuts API allowing us to have a single Utility Class that converts API models to + * Shortcut Helper models + * + * @param label Equivalent to shortcut helper's subcategory label + * @param items Keyboard Shortcuts received from API + * @param packageName package name of current app shortcut if available. + */ +data class InternalKeyboardShortcutGroup( + val label: String, + val items: List<InternalKeyboardShortcutInfo>, + val packageName: String? = null, +) + +/** + * @param label Shortcut label + * @param keycode Key to trigger shortcut + * @param modifiers Mask of shortcut modifiers + * @param baseCharacter Key to trigger shortcut if is a character + * @param icon Shortcut icon if available - often used for app launch shortcuts + * @param isCustomShortcut If Shortcut is user customized or system defined. + */ +data class InternalKeyboardShortcutInfo( + val label: String, + val keycode: Int, + val modifiers: Int, + val baseCharacter: Char = Char.MIN_VALUE, + val icon: Icon? = null, + val isCustomShortcut: Boolean = false, +) diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepository.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepository.kt new file mode 100644 index 000000000000..7f8fbb5065aa --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepository.kt @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.keyboard.shortcut.data.repository + +import android.content.Context +import android.content.Context.INPUT_SERVICE +import android.hardware.input.InputGestureData +import android.hardware.input.InputGestureData.KeyTrigger +import android.hardware.input.InputManager +import android.hardware.input.InputSettings +import android.hardware.input.KeyGestureEvent +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.keyboard.shortcut.data.model.InternalKeyboardShortcutGroup +import com.android.systemui.keyboard.shortcut.data.model.InternalKeyboardShortcutInfo +import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategory +import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType +import com.android.systemui.keyboard.shortcut.shared.model.ShortcutHelperState.Active +import com.android.systemui.settings.UserTracker +import javax.inject.Inject +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.withContext + +@SysUISingleton +class CustomShortcutCategoriesRepository +@Inject +constructor( + stateRepository: ShortcutHelperStateRepository, + private val userTracker: UserTracker, + @Background private val backgroundScope: CoroutineScope, + @Background private val backgroundDispatcher: CoroutineDispatcher, + private val shortcutCategoriesUtils: ShortcutCategoriesUtils, +) : ShortcutCategoriesRepository { + + private val userContext: Context + get() = userTracker.createCurrentUserContext(userTracker.userContext) + + // Input manager created with user context to provide correct user id when requesting custom + // shortcut + private val inputManager: InputManager + get() = userContext.getSystemService(INPUT_SERVICE) as InputManager + + private val activeInputDevice = + stateRepository.state.map { + if (it is Active) { + withContext(backgroundDispatcher) { inputManager.getInputDevice(it.deviceId) } + } else { + null + } + } + + override val categories: Flow<List<ShortcutCategory>> = + activeInputDevice + .map { inputDevice -> + if (inputDevice == null) { + emptyList() + } else { + val customInputGesturesForUser: List<InputGestureData> = + if (InputSettings.isCustomizableInputGesturesFeatureFlagEnabled()) { + inputManager.getCustomInputGestures(/* filter= */ null) + } else emptyList() + val sources = toInternalGroupSources(customInputGesturesForUser) + val supportedKeyCodes = + shortcutCategoriesUtils.fetchSupportedKeyCodes( + inputDevice.id, + sources.map { it.groups }, + ) + + val result = + sources.mapNotNull { source -> + shortcutCategoriesUtils.fetchShortcutCategory( + type = source.type, + groups = source.groups, + inputDevice = inputDevice, + supportedKeyCodes = supportedKeyCodes, + ) + } + result + } + } + .stateIn( + scope = backgroundScope, + initialValue = emptyList(), + started = SharingStarted.Lazily, + ) + + private fun toInternalGroupSources( + inputGestures: List<InputGestureData> + ): List<InternalGroupsSource> { + val ungroupedInternalGroupSources = + inputGestures.mapNotNull { gestureData -> + val keyTrigger = gestureData.trigger as KeyTrigger + val keyGestureType = gestureData.action.keyGestureType() + fetchGroupLabelByGestureType(keyGestureType)?.let { groupLabel -> + toInternalKeyboardShortcutInfo(keyGestureType, keyTrigger)?.let { + internalKeyboardShortcutInfo -> + val group = + InternalKeyboardShortcutGroup( + label = groupLabel, + items = listOf(internalKeyboardShortcutInfo), + ) + + fetchShortcutCategoryTypeByGestureType(keyGestureType)?.let { + InternalGroupsSource(groups = listOf(group), type = it) + } + } + } + } + + return ungroupedInternalGroupSources + } + + private fun toInternalKeyboardShortcutInfo( + keyGestureType: Int, + keyTrigger: KeyTrigger, + ): InternalKeyboardShortcutInfo? { + fetchShortcutInfoLabelByGestureType(keyGestureType)?.let { + return InternalKeyboardShortcutInfo( + label = it, + keycode = keyTrigger.keycode, + modifiers = keyTrigger.modifierState, + isCustomShortcut = true, + ) + } + return null + } + + private fun fetchGroupLabelByGestureType( + @KeyGestureEvent.KeyGestureType keyGestureType: Int + ): String? { + return InputGestures.gestureToInternalKeyboardShortcutGroupLabelMap.getOrDefault( + keyGestureType, + null, + ) + } + + private fun fetchShortcutInfoLabelByGestureType( + @KeyGestureEvent.KeyGestureType keyGestureType: Int + ): String? { + return InputGestures.gestureToInternalKeyboardShortcutInfoLabelMap.getOrDefault( + keyGestureType, + null, + ) + } + + private fun fetchShortcutCategoryTypeByGestureType( + @KeyGestureEvent.KeyGestureType keyGestureType: Int + ): ShortcutCategoryType? { + return InputGestures.gestureToShortcutCategoryTypeMap.getOrDefault(keyGestureType, null) + } + + private data class InternalGroupsSource( + val groups: List<InternalKeyboardShortcutGroup>, + val type: ShortcutCategoryType, + ) +} diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/DefaultShortcutCategoriesRepository.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/DefaultShortcutCategoriesRepository.kt new file mode 100644 index 000000000000..5bb7cdd03b8f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/DefaultShortcutCategoriesRepository.kt @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.keyboard.shortcut.data.repository + +import android.hardware.input.InputManager +import android.view.KeyboardShortcutGroup +import android.view.KeyboardShortcutInfo +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.keyboard.shortcut.data.model.InternalKeyboardShortcutGroup +import com.android.systemui.keyboard.shortcut.data.model.InternalKeyboardShortcutInfo +import com.android.systemui.keyboard.shortcut.data.source.KeyboardShortcutGroupsSource +import com.android.systemui.keyboard.shortcut.qualifiers.AppCategoriesShortcuts +import com.android.systemui.keyboard.shortcut.qualifiers.CurrentAppShortcuts +import com.android.systemui.keyboard.shortcut.qualifiers.InputShortcuts +import com.android.systemui.keyboard.shortcut.qualifiers.MultitaskingShortcuts +import com.android.systemui.keyboard.shortcut.qualifiers.SystemShortcuts +import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategory +import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType +import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.AppCategories +import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.CurrentApp +import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.InputMethodEditor +import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.MultiTasking +import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.System +import com.android.systemui.keyboard.shortcut.shared.model.ShortcutHelperState.Active +import javax.inject.Inject +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.withContext + +@SysUISingleton +class DefaultShortcutCategoriesRepository +@Inject +constructor( + @Background private val backgroundScope: CoroutineScope, + @Background private val backgroundDispatcher: CoroutineDispatcher, + @SystemShortcuts private val systemShortcutsSource: KeyboardShortcutGroupsSource, + @MultitaskingShortcuts private val multitaskingShortcutsSource: KeyboardShortcutGroupsSource, + @AppCategoriesShortcuts private val appCategoriesShortcutsSource: KeyboardShortcutGroupsSource, + @InputShortcuts private val inputShortcutsSource: KeyboardShortcutGroupsSource, + @CurrentAppShortcuts private val currentAppShortcutsSource: KeyboardShortcutGroupsSource, + private val inputManager: InputManager, + stateRepository: ShortcutHelperStateRepository, + shortcutCategoriesUtils: ShortcutCategoriesUtils, +) : ShortcutCategoriesRepository { + + private val sources = + listOf( + InternalGroupsSource(source = systemShortcutsSource, typeProvider = { System }), + InternalGroupsSource( + source = multitaskingShortcutsSource, + typeProvider = { MultiTasking }, + ), + InternalGroupsSource( + source = appCategoriesShortcutsSource, + typeProvider = { AppCategories }, + ), + InternalGroupsSource( + source = inputShortcutsSource, + typeProvider = { InputMethodEditor }, + ), + InternalGroupsSource( + source = currentAppShortcutsSource, + typeProvider = { groups -> getCurrentAppShortcutCategoryType(groups) }, + ), + ) + + private val activeInputDevice = + stateRepository.state.map { + if (it is Active) { + withContext(backgroundDispatcher) { inputManager.getInputDevice(it.deviceId) } + } else { + null + } + } + + override val categories: Flow<List<ShortcutCategory>> = + activeInputDevice + .map { inputDevice -> + if (inputDevice == null) { + return@map emptyList() + } + val groupsFromAllSources = + sources.map { + toInternalKeyboardShortcutGroups(it.source.shortcutGroups(inputDevice.id)) + } + val supportedKeyCodes = + shortcutCategoriesUtils.fetchSupportedKeyCodes( + inputDevice.id, + groupsFromAllSources, + ) + return@map sources.mapIndexedNotNull { index, internalGroupsSource -> + shortcutCategoriesUtils.fetchShortcutCategory( + internalGroupsSource.typeProvider(groupsFromAllSources[index]), + groupsFromAllSources[index], + inputDevice, + supportedKeyCodes, + ) + } + } + .stateIn( + scope = backgroundScope, + started = SharingStarted.Lazily, + initialValue = emptyList(), + ) + + private fun toInternalKeyboardShortcutGroups( + keyboardShortcutGroups: List<KeyboardShortcutGroup> + ): List<InternalKeyboardShortcutGroup> { + return keyboardShortcutGroups.map { group -> + InternalKeyboardShortcutGroup( + label = group.label.toString(), + items = group.items.map { toInternalKeyboardShortcutInfo(it) }, + packageName = group.packageName?.toString(), + ) + } + } + + private fun toInternalKeyboardShortcutInfo( + keyboardShortcutInfo: KeyboardShortcutInfo + ): InternalKeyboardShortcutInfo { + return InternalKeyboardShortcutInfo( + label = keyboardShortcutInfo.label!!.toString(), + keycode = keyboardShortcutInfo.keycode, + modifiers = keyboardShortcutInfo.modifiers, + baseCharacter = keyboardShortcutInfo.baseCharacter, + icon = keyboardShortcutInfo.icon, + ) + } + + private fun getCurrentAppShortcutCategoryType( + shortcutGroups: List<InternalKeyboardShortcutGroup> + ): ShortcutCategoryType? { + val packageName = shortcutGroups.firstOrNull()?.packageName ?: return null + return CurrentApp(packageName) + } + + private class InternalGroupsSource( + val source: KeyboardShortcutGroupsSource, + val typeProvider: (groups: List<InternalKeyboardShortcutGroup>) -> ShortcutCategoryType?, + ) +} diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestures.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestures.kt new file mode 100644 index 000000000000..28134db4d588 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestures.kt @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.keyboard.shortcut.data.repository + +import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS +import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_BACK +import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_LEFT +import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT +import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_HOME +import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT +import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER +import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR +import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR +import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS +import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL +import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS +import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING +import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS +import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT +import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_LOCK_SCREEN +import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION +import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_NOTES +import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER +import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS +import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER +import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT +import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT +import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT +import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL +import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.AppCategories +import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.MultiTasking +import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.System + +object InputGestures { + val gestureToShortcutCategoryTypeMap = + mapOf( + // System Category + KEY_GESTURE_TYPE_HOME to System, + KEY_GESTURE_TYPE_RECENT_APPS to System, + KEY_GESTURE_TYPE_BACK to System, + KEY_GESTURE_TYPE_TAKE_SCREENSHOT to System, + KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER to System, + KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL to System, + KEY_GESTURE_TYPE_LOCK_SCREEN to System, + KEY_GESTURE_TYPE_OPEN_NOTES to System, + KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS to System, + KEY_GESTURE_TYPE_LAUNCH_ASSISTANT to System, + KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT to System, + KEY_GESTURE_TYPE_ALL_APPS to System, + + // Multitasking Category + KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER to MultiTasking, + KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT to MultiTasking, + KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT to MultiTasking, + KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION to MultiTasking, + KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_LEFT to MultiTasking, + KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT to MultiTasking, + + // App Category + KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR to AppCategories, + KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR to AppCategories, + KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER to AppCategories, + KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS to AppCategories, + KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL to AppCategories, + KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS to AppCategories, + KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING to AppCategories, + ) + + // TODO move all string to to resources use the same resources as the original shortcuts + // - that way when the strings are translated there are no discrepancies + val gestureToInternalKeyboardShortcutGroupLabelMap = + mapOf( + // System Category + KEY_GESTURE_TYPE_HOME to "System controls", + KEY_GESTURE_TYPE_RECENT_APPS to "System controls", + KEY_GESTURE_TYPE_BACK to "System controls", + KEY_GESTURE_TYPE_TAKE_SCREENSHOT to "System controls", + KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER to "System controls", + KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL to "System controls", + KEY_GESTURE_TYPE_LOCK_SCREEN to "System controls", + KEY_GESTURE_TYPE_ALL_APPS to "System controls", + KEY_GESTURE_TYPE_OPEN_NOTES to "System apps", + KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS to "System apps", + KEY_GESTURE_TYPE_LAUNCH_ASSISTANT to "System apps", + KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT to "System apps", + + // Multitasking Category + KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER to "Recent apps", + KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT to "Split screen", + KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT to "Split screen", + KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION to "Split screen", + KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_LEFT to "Split screen", + KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT to "Split screen", + + // App Category + KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR to "Applications", + KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR to "Applications", + KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER to "Applications", + KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS to "Applications", + KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL to "Applications", + KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS to "Applications", + KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING to "Applications", + ) + + val gestureToInternalKeyboardShortcutInfoLabelMap = + mapOf( + // System Category + KEY_GESTURE_TYPE_HOME to "Go to home screen", + KEY_GESTURE_TYPE_RECENT_APPS to "View recent apps", + KEY_GESTURE_TYPE_BACK to "Go back", + KEY_GESTURE_TYPE_TAKE_SCREENSHOT to "Take screenshot", + KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER to "Show shortcuts", + KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL to "View notifications", + KEY_GESTURE_TYPE_LOCK_SCREEN to "Lock screen", + KEY_GESTURE_TYPE_ALL_APPS to "Open apps list", + KEY_GESTURE_TYPE_OPEN_NOTES to "Take a note", + KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS to "Open settings", + KEY_GESTURE_TYPE_LAUNCH_ASSISTANT to "Open assistant", + KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT to "Open assistant", + + // Multitasking Category + KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER to "Cycle forward through recent apps", + KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT to + "Use split screen with current app on the left", + KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT to + "Use split screen with current app on the right", + KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION to "Switch from split screen to full screen", + KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_LEFT to + "Switch to app on left or above while using split screen", + KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT to + "Switch to app on right or below while using split screen", + + // App Category + KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR to "Calculator", + KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR to "Calendar", + KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER to "Chrome", + KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS to "Contacts", + KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL to "Gmail", + KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS to "Maps", + KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING to "Messages", + ) +} diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutCategoriesRepository.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutCategoriesRepository.kt new file mode 100644 index 000000000000..2e8cc000a63c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutCategoriesRepository.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.keyboard.shortcut.data.repository + +import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategory +import kotlinx.coroutines.flow.Flow + +interface ShortcutCategoriesRepository { + val categories: Flow<List<ShortcutCategory>> +} diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperCategoriesRepository.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutCategoriesUtils.kt index 12dd58176a71..899fd15d6115 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperCategoriesRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutCategoriesUtils.kt @@ -24,123 +24,34 @@ import android.util.Log import android.view.InputDevice import android.view.KeyCharacterMap import android.view.KeyEvent -import android.view.KeyboardShortcutGroup -import android.view.KeyboardShortcutInfo import com.android.systemui.Flags.shortcutHelperKeyGlyph -import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background -import com.android.systemui.keyboard.shortcut.data.source.KeyboardShortcutGroupsSource -import com.android.systemui.keyboard.shortcut.qualifiers.AppCategoriesShortcuts -import com.android.systemui.keyboard.shortcut.qualifiers.CurrentAppShortcuts -import com.android.systemui.keyboard.shortcut.qualifiers.InputShortcuts -import com.android.systemui.keyboard.shortcut.qualifiers.MultitaskingShortcuts -import com.android.systemui.keyboard.shortcut.qualifiers.SystemShortcuts +import com.android.systemui.keyboard.shortcut.data.model.InternalKeyboardShortcutGroup +import com.android.systemui.keyboard.shortcut.data.model.InternalKeyboardShortcutInfo import com.android.systemui.keyboard.shortcut.shared.model.Shortcut import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategory import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType -import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.AppCategories -import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.CurrentApp -import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.InputMethodEditor -import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.MultiTasking -import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.System import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCommand -import com.android.systemui.keyboard.shortcut.shared.model.ShortcutHelperState.Active import com.android.systemui.keyboard.shortcut.shared.model.ShortcutIcon import com.android.systemui.keyboard.shortcut.shared.model.ShortcutKey import com.android.systemui.keyboard.shortcut.shared.model.ShortcutSubCategory import javax.inject.Inject -import kotlinx.coroutines.CoroutineDispatcher -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.stateIn +import kotlin.coroutines.CoroutineContext import kotlinx.coroutines.withContext -@SysUISingleton -class ShortcutHelperCategoriesRepository +class ShortcutCategoriesUtils @Inject constructor( private val context: Context, - @Background private val backgroundScope: CoroutineScope, - @Background private val backgroundDispatcher: CoroutineDispatcher, - @SystemShortcuts private val systemShortcutsSource: KeyboardShortcutGroupsSource, - @MultitaskingShortcuts private val multitaskingShortcutsSource: KeyboardShortcutGroupsSource, - @AppCategoriesShortcuts private val appCategoriesShortcutsSource: KeyboardShortcutGroupsSource, - @InputShortcuts private val inputShortcutsSource: KeyboardShortcutGroupsSource, - @CurrentAppShortcuts private val currentAppShortcutsSource: KeyboardShortcutGroupsSource, + @Background private val backgroundCoroutineContext: CoroutineContext, private val inputManager: InputManager, - stateRepository: ShortcutHelperStateRepository, ) { - - private val sources = - listOf( - InternalGroupsSource( - source = systemShortcutsSource, - isTrusted = true, - typeProvider = { System }, - ), - InternalGroupsSource( - source = multitaskingShortcutsSource, - isTrusted = true, - typeProvider = { MultiTasking }, - ), - InternalGroupsSource( - source = appCategoriesShortcutsSource, - isTrusted = true, - typeProvider = { AppCategories }, - ), - InternalGroupsSource( - source = inputShortcutsSource, - isTrusted = false, - typeProvider = { InputMethodEditor }, - ), - InternalGroupsSource( - source = currentAppShortcutsSource, - isTrusted = false, - typeProvider = { groups -> getCurrentAppShortcutCategoryType(groups) }, - ), - ) - - private val activeInputDevice = - stateRepository.state.map { - if (it is Active) { - withContext(backgroundDispatcher) { inputManager.getInputDevice(it.deviceId) } - } else { - null - } - } - - val categories: Flow<List<ShortcutCategory>> = - activeInputDevice - .map { inputDevice -> - if (inputDevice == null) { - return@map emptyList() - } - val groupsFromAllSources = sources.map { it.source.shortcutGroups(inputDevice.id) } - val supportedKeyCodes = fetchSupportedKeyCodes(inputDevice.id, groupsFromAllSources) - return@map sources.mapIndexedNotNull { index, internalGroupsSource -> - fetchShortcutCategory( - internalGroupsSource, - groupsFromAllSources[index], - inputDevice, - supportedKeyCodes, - ) - } - } - .stateIn( - scope = backgroundScope, - started = SharingStarted.Lazily, - initialValue = emptyList(), - ) - - private fun fetchShortcutCategory( - internalGroupsSource: InternalGroupsSource, - groups: List<KeyboardShortcutGroup>, + fun fetchShortcutCategory( + type: ShortcutCategoryType?, + groups: List<InternalKeyboardShortcutGroup>, inputDevice: InputDevice, supportedKeyCodes: Set<Int>, ): ShortcutCategory? { - val type = internalGroupsSource.typeProvider(groups) return if (type == null) { null } else { @@ -151,27 +62,17 @@ constructor( inputDevice.keyCharacterMap, type, groups, - internalGroupsSource.isTrusted, + type.isTrusted, supportedKeyCodes, ) } } - private fun getCurrentAppShortcutCategoryType( - shortcutGroups: List<KeyboardShortcutGroup> - ): ShortcutCategoryType? { - return if (shortcutGroups.isEmpty()) { - null - } else { - CurrentApp(packageName = shortcutGroups[0].packageName.toString()) - } - } - private fun toShortcutCategory( keyGlyphMap: KeyGlyphMap?, keyCharacterMap: KeyCharacterMap, type: ShortcutCategoryType, - shortcutGroups: List<KeyboardShortcutGroup>, + shortcutGroups: List<InternalKeyboardShortcutGroup>, keepIcons: Boolean, supportedKeyCodes: Set<Int>, ): ShortcutCategory? { @@ -179,7 +80,7 @@ constructor( shortcutGroups .map { shortcutGroup -> ShortcutSubCategory( - shortcutGroup.label.toString(), + shortcutGroup.label, toShortcuts( keyGlyphMap, keyCharacterMap, @@ -201,7 +102,7 @@ constructor( private fun toShortcuts( keyGlyphMap: KeyGlyphMap?, keyCharacterMap: KeyCharacterMap, - infoList: List<KeyboardShortcutInfo>, + infoList: List<InternalKeyboardShortcutInfo>, keepIcons: Boolean, supportedKeyCodes: Set<Int>, ) = @@ -216,13 +117,13 @@ constructor( private fun toShortcut( keyGlyphMap: KeyGlyphMap?, keyCharacterMap: KeyCharacterMap, - shortcutInfo: KeyboardShortcutInfo, + shortcutInfo: InternalKeyboardShortcutInfo, keepIcon: Boolean, ): Shortcut? { val shortcutCommand = toShortcutCommand(keyGlyphMap, keyCharacterMap, shortcutInfo) ?: return null return Shortcut( - label = shortcutInfo.label!!.toString(), + label = shortcutInfo.label, icon = toShortcutIcon(keepIcon, shortcutInfo), commands = listOf(shortcutCommand), ) @@ -230,7 +131,7 @@ constructor( private fun toShortcutIcon( keepIcon: Boolean, - shortcutInfo: KeyboardShortcutInfo, + shortcutInfo: InternalKeyboardShortcutInfo, ): ShortcutIcon? { if (!keepIcon) { return null @@ -247,7 +148,7 @@ constructor( private fun toShortcutCommand( keyGlyphMap: KeyGlyphMap?, keyCharacterMap: KeyCharacterMap, - info: KeyboardShortcutInfo, + info: InternalKeyboardShortcutInfo, ): ShortcutCommand? { val keys = mutableListOf<ShortcutKey>() var remainingModifiers = info.modifiers @@ -272,7 +173,7 @@ constructor( Log.wtf(TAG, "No keys for $info") return null } - return ShortcutCommand(keys) + return ShortcutCommand(keys = keys, isCustom = info.isCustomShortcut) } private fun toShortcutModifierKey(keyGlyphMap: KeyGlyphMap?, modifierMask: Int): ShortcutKey? { @@ -325,11 +226,11 @@ constructor( return null } - private suspend fun fetchSupportedKeyCodes( + suspend fun fetchSupportedKeyCodes( deviceId: Int, - groupsFromAllSources: List<List<KeyboardShortcutGroup>>, + groupsFromAllSources: List<List<InternalKeyboardShortcutGroup>>, ): Set<Int> = - withContext(backgroundDispatcher) { + withContext(backgroundCoroutineContext) { val allUsedKeyCodes = groupsFromAllSources .flatMap { groups -> groups.flatMap { group -> group.items } } @@ -342,14 +243,8 @@ constructor( .toSet() } - private class InternalGroupsSource( - val source: KeyboardShortcutGroupsSource, - val isTrusted: Boolean, - val typeProvider: (groups: List<KeyboardShortcutGroup>) -> ShortcutCategoryType?, - ) - companion object { - private const val TAG = "SHCategoriesRepo" + private const val TAG = "ShortcutCategoriesUtils" private val SUPPORTED_MODIFIERS = listOf( 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 6f19561dd87b..39fc27d35082 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 @@ -17,7 +17,7 @@ package com.android.systemui.keyboard.shortcut.domain.interactor import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.keyboard.shortcut.data.repository.ShortcutHelperCategoriesRepository +import com.android.systemui.keyboard.shortcut.data.repository.DefaultShortcutCategoriesRepository 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.ShortcutSubCategory @@ -28,9 +28,7 @@ import kotlinx.coroutines.flow.map @SysUISingleton class ShortcutHelperCategoriesInteractor @Inject -constructor( - categoriesRepository: ShortcutHelperCategoriesRepository, -) { +constructor(categoriesRepository: DefaultShortcutCategoriesRepository) { val shortcutCategories: Flow<List<ShortcutCategory>> = categoriesRepository.categories.map { categories -> @@ -42,12 +40,12 @@ constructor( shortcutCategory.subCategories.map { ShortcutSubCategory( label = it.label, - shortcuts = groupShortcutsInSubcategory(it.shortcuts) + shortcuts = groupShortcutsInSubcategory(it.shortcuts), ) } return ShortcutCategory( type = shortcutCategory.type, - subCategories = subCategoriesWithGroupedShortcuts + subCategories = subCategoriesWithGroupedShortcuts, ) } @@ -59,7 +57,7 @@ constructor( Shortcut( label = commonLabel, icon = groupedShortcuts.firstOrNull()?.icon, - commands = groupedShortcuts.flatMap { it.commands } + commands = groupedShortcuts.flatMap { it.commands }, ) } } diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCategory.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCategory.kt index c89ef1582f98..813a1fcac65d 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCategory.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCategory.kt @@ -17,24 +17,36 @@ package com.android.systemui.keyboard.shortcut.shared.model sealed interface ShortcutCategoryType { - data object System : ShortcutCategoryType + val isTrusted: Boolean - data object MultiTasking : ShortcutCategoryType + data object System : ShortcutCategoryType { + override val isTrusted: Boolean = true + } + + data object MultiTasking : ShortcutCategoryType { + override val isTrusted: Boolean = true + } - data object InputMethodEditor : ShortcutCategoryType + data object InputMethodEditor : ShortcutCategoryType { + override val isTrusted: Boolean = false + } - data object AppCategories : ShortcutCategoryType + data object AppCategories : ShortcutCategoryType { + override val isTrusted: Boolean = true + } - data class CurrentApp(val packageName: String) : ShortcutCategoryType + data class CurrentApp(val packageName: String) : ShortcutCategoryType { + override val isTrusted: Boolean = false + } } data class ShortcutCategory( val type: ShortcutCategoryType, - val subCategories: List<ShortcutSubCategory> + val subCategories: List<ShortcutSubCategory>, ) { constructor( type: ShortcutCategoryType, - vararg subCategories: ShortcutSubCategory + vararg subCategories: ShortcutSubCategory, ) : this(type, subCategories.asList()) } diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCommand.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCommand.kt index 28451ae2bc14..c7e6b43b9624 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCommand.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/ShortcutCommand.kt @@ -18,10 +18,11 @@ package com.android.systemui.keyboard.shortcut.shared.model import androidx.annotation.DrawableRes -data class ShortcutCommand(val keys: List<ShortcutKey>) +data class ShortcutCommand(val keys: List<ShortcutKey>, val isCustom: Boolean = false) class ShortcutCommandBuilder { private val keys = mutableListOf<ShortcutKey>() + private var isCustom = false fun key(text: String) { keys += ShortcutKey.Text(text) @@ -31,7 +32,11 @@ class ShortcutCommandBuilder { keys += ShortcutKey.Icon.ResIdIcon(drawableResId) } - fun build() = ShortcutCommand(keys) + fun isCustom(isCustom: Boolean) { + this.isCustom = isCustom + } + + fun build() = ShortcutCommand(keys, isCustom) } fun shortcutCommand(block: ShortcutCommandBuilder.() -> Unit) = diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperDialogStarter.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperDialogStarter.kt index 807c70bbc225..10a201e976c1 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperDialogStarter.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperDialogStarter.kt @@ -27,6 +27,7 @@ import androidx.compose.foundation.layout.width import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.systemui.CoreStartable import com.android.systemui.dagger.SysUISingleton @@ -36,6 +37,7 @@ import com.android.systemui.keyboard.shortcut.ui.composable.ShortcutHelperBottom import com.android.systemui.keyboard.shortcut.ui.composable.getWidth import com.android.systemui.keyboard.shortcut.ui.viewmodel.ShortcutHelperViewModel import com.android.systemui.plugins.ActivityStarter +import com.android.systemui.res.R import com.android.systemui.statusbar.phone.SystemUIDialogFactory import com.android.systemui.statusbar.phone.createBottomSheet import javax.inject.Inject @@ -86,6 +88,7 @@ constructor( }, ) dialog.setOnDismissListener { shortcutHelperViewModel.onViewClosed() } + dialog.setTitle(stringResource(R.string.shortcut_helper_title)) }, maxWidth = ShortcutHelperBottomSheet.LargeScreenWidthLandscape, ) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 60a306b3e245..2ee9ddb0e453 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -239,7 +239,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, private static final boolean ENABLE_NEW_KEYGUARD_SHELL_TRANSITIONS = Flags.ensureKeyguardDoesTransitionStarting(); - private static final int KEYGUARD_DISPLAY_TIMEOUT_DELAY_DEFAULT = 30000; + public static final int KEYGUARD_DISPLAY_TIMEOUT_DELAY_DEFAULT = 30000; private static final long KEYGUARD_DONE_PENDING_TIMEOUT_MS = 3000; private static final boolean DEBUG = KeyguardConstants.DEBUG; diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt index 3a5614fbc430..eaf8fa9585f6 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt @@ -114,6 +114,18 @@ interface KeyguardTransitionRepository { @FloatRange(from = 0.0, to = 1.0) value: Float, state: TransitionState, ) + + /** + * Forces the current transition to emit FINISHED, foregoing any additional RUNNING steps that + * otherwise would have been emitted. + * + * When the screen is off, upcoming performance changes cause all Animators to cease emitting + * frames, which means the Animator passed to [startTransition] will never finish if it was + * running when the screen turned off. Also, there's simply no reason to emit RUNNING steps when + * the screen isn't even on. As long as we emit FINISHED, everything should end up in the + * correct state. + */ + suspend fun forceFinishCurrentTransition() } @SysUISingleton @@ -134,6 +146,7 @@ constructor(@Main val mainDispatcher: CoroutineDispatcher) : KeyguardTransitionR override val transitions = _transitions.asSharedFlow().distinctUntilChanged() private var lastStep: TransitionStep = TransitionStep() private var lastAnimator: ValueAnimator? = null + private var animatorListener: AnimatorListenerAdapter? = null private val withContextMutex = Mutex() private val _currentTransitionInfo: MutableStateFlow<TransitionInfo> = @@ -233,7 +246,7 @@ constructor(@Main val mainDispatcher: CoroutineDispatcher) : KeyguardTransitionR ) } - val adapter = + animatorListener = object : AnimatorListenerAdapter() { override fun onAnimationStart(animation: Animator) { emitTransition( @@ -254,9 +267,10 @@ constructor(@Main val mainDispatcher: CoroutineDispatcher) : KeyguardTransitionR animator.removeListener(this) animator.removeUpdateListener(updateListener) lastAnimator = null + animatorListener = null } } - animator.addListener(adapter) + animator.addListener(animatorListener) animator.addUpdateListener(updateListener) animator.start() return@withContext null @@ -290,6 +304,33 @@ constructor(@Main val mainDispatcher: CoroutineDispatcher) : KeyguardTransitionR } } + override suspend fun forceFinishCurrentTransition() { + withContextMutex.lock() + + if (lastAnimator?.isRunning != true) { + return + } + + return withContext("$TAG#forceFinishCurrentTransition", mainDispatcher) { + withContextMutex.unlock() + + Log.d(TAG, "forceFinishCurrentTransition() - emitting FINISHED early.") + + lastAnimator?.apply { + // Cancel the animator, but remove listeners first so we don't emit CANCELED. + removeAllListeners() + cancel() + + // Emit a final 1f RUNNING step to ensure that any transitions not listening for a + // FINISHED step end up in the right end state. + emitTransition(TransitionStep(currentTransitionInfo, 1f, TransitionState.RUNNING)) + + // Ask the listener to emit FINISHED and clean up its state. + animatorListener?.onAnimationEnd(this) + } + } + } + private suspend fun updateTransitionInternal( transitionId: UUID, @FloatRange(from = 0.0, to = 1.0) value: Float, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt index 21afd3e4c444..8c9473f4284b 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt @@ -430,7 +430,9 @@ constructor( ), KeyguardPickerFlag( name = Contract.FlagsTable.FLAG_NAME_CUSTOM_CLOCKS_ENABLED, - value = featureFlags.isEnabled(Flags.LOCKSCREEN_CUSTOM_CLOCKS), + value = + com.android.systemui.Flags.lockscreenCustomClocks() || + featureFlags.isEnabled(Flags.LOCKSCREEN_CUSTOM_CLOCKS), ), KeyguardPickerFlag( name = Contract.FlagsTable.FLAG_NAME_WALLPAPER_FULLSCREEN_PREVIEW, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt index b815f1988e7e..7cd2744cb7dc 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt @@ -19,8 +19,10 @@ package com.android.systemui.keyguard.domain.interactor import android.annotation.SuppressLint import android.util.Log +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.compose.animation.scene.ObservableTransitionState import com.android.compose.animation.scene.SceneKey +import com.android.systemui.Flags.keyguardTransitionForceFinishOnScreenOff import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository @@ -30,6 +32,8 @@ import com.android.systemui.keyguard.shared.model.KeyguardState.OFF import com.android.systemui.keyguard.shared.model.KeyguardState.UNDEFINED import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionStep +import com.android.systemui.power.domain.interactor.PowerInteractor +import com.android.systemui.power.shared.model.ScreenPowerState import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.scene.shared.model.Scenes @@ -59,7 +63,6 @@ import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.shareIn import kotlinx.coroutines.flow.stateIn -import com.android.app.tracing.coroutines.launchTraced as launch /** Encapsulates business-logic related to the keyguard transitions. */ @OptIn(ExperimentalCoroutinesApi::class) @@ -70,6 +73,7 @@ constructor( @Application val scope: CoroutineScope, private val repository: KeyguardTransitionRepository, private val sceneInteractor: SceneInteractor, + private val powerInteractor: PowerInteractor, ) { private val transitionMap = mutableMapOf<Edge.StateToState, MutableSharedFlow<TransitionStep>>() @@ -188,6 +192,18 @@ constructor( } } } + + if (keyguardTransitionForceFinishOnScreenOff()) { + /** + * If the screen is turning off, finish the current transition immediately. Further + * frames won't be visible anyway. + */ + scope.launch { + powerInteractor.screenPowerState + .filter { it == ScreenPowerState.SCREEN_TURNING_OFF } + .collect { repository.forceFinishCurrentTransition() } + } + } } fun transition(edge: Edge, edgeWithoutSceneContainer: Edge? = null): Flow<TransitionStep> { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt index 7292ddaf43a7..b30e1e9b1103 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt @@ -26,6 +26,7 @@ import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintSet import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.keyguard.MigrateClocksToBlueprint import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor @@ -44,7 +45,6 @@ import kotlinx.coroutines.DisposableHandle import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map -import com.android.app.tracing.coroutines.launchTraced as launch object KeyguardClockViewBinder { private val TAG = KeyguardClockViewBinder::class.simpleName!! @@ -133,15 +133,26 @@ object KeyguardClockViewBinder { launch { if (!MigrateClocksToBlueprint.isEnabled) return@launch aodBurnInViewModel.movement.collect { burnInModel -> - viewModel.currentClock.value?.let { - it.largeClock.layout.applyAodBurnIn( + viewModel.currentClock.value + ?.largeClock + ?.layout + ?.applyAodBurnIn( AodClockBurnInModel( translationX = burnInModel.translationX.toFloat(), translationY = burnInModel.translationY.toFloat(), scale = burnInModel.scale, ) ) - } + } + } + + launch { + if (!MigrateClocksToBlueprint.isEnabled) return@launch + viewModel.largeClockTextSize.collect { fontSizePx -> + viewModel.currentClock.value + ?.largeClock + ?.events + ?.onFontSettingChanged(fontSizePx = fontSizePx.toFloat()) } } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt index 46f5c05092eb..914fdd20e48e 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt @@ -18,34 +18,23 @@ package com.android.systemui.keyguard.ui.binder import android.content.Context -import android.util.DisplayMetrics import android.view.View import android.view.View.INVISIBLE import android.view.View.VISIBLE import android.view.ViewGroup -import android.view.ViewGroup.LayoutParams.WRAP_CONTENT import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintSet -import androidx.constraintlayout.widget.ConstraintSet.BOTTOM -import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID -import androidx.constraintlayout.widget.ConstraintSet.START -import androidx.constraintlayout.widget.ConstraintSet.TOP import androidx.core.view.isVisible import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle import com.android.app.tracing.coroutines.launchTraced as launch -import com.android.internal.policy.SystemBarUtils -import com.android.systemui.customization.R as customR import com.android.systemui.keyguard.shared.model.ClockSizeSetting import com.android.systemui.keyguard.ui.preview.KeyguardPreviewRenderer -import com.android.systemui.keyguard.ui.view.layout.sections.ClockSection.Companion.getDimen import com.android.systemui.keyguard.ui.view.layout.sections.setVisibility import com.android.systemui.keyguard.ui.viewmodel.KeyguardPreviewClockViewModel import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.plugins.clocks.ClockController -import com.android.systemui.res.R import com.android.systemui.shared.clocks.ClockRegistry -import com.android.systemui.util.Utils import kotlin.reflect.KSuspendFunction1 /** Binder for the small clock view, large clock view. */ @@ -131,78 +120,6 @@ object KeyguardPreviewClockViewBinder { } } - private fun applyClockDefaultConstraints(context: Context, constraints: ConstraintSet) { - constraints.apply { - constrainWidth(customR.id.lockscreen_clock_view_large, ConstraintSet.WRAP_CONTENT) - // The following two lines make lockscreen_clock_view_large is constrained to available - // height when it goes beyond constraints; otherwise, it use WRAP_CONTENT - constrainHeight(customR.id.lockscreen_clock_view_large, WRAP_CONTENT) - constrainMaxHeight(customR.id.lockscreen_clock_view_large, 0) - val largeClockTopMargin = - SystemBarUtils.getStatusBarHeight(context) + - context.resources.getDimensionPixelSize(customR.dimen.small_clock_padding_top) + - context.resources.getDimensionPixelSize( - R.dimen.keyguard_smartspace_top_offset - ) + - getDimen(context, DATE_WEATHER_VIEW_HEIGHT) + - getDimen(context, ENHANCED_SMARTSPACE_HEIGHT) - connect( - customR.id.lockscreen_clock_view_large, - TOP, - PARENT_ID, - TOP, - largeClockTopMargin, - ) - connect(customR.id.lockscreen_clock_view_large, START, PARENT_ID, START) - connect( - customR.id.lockscreen_clock_view_large, - ConstraintSet.END, - PARENT_ID, - ConstraintSet.END, - ) - - // In preview, we'll show UDFPS icon for UDFPS devices and nothing for non-UDFPS - // devices, but we need position of device entry icon to constrain clock - if (getConstraint(lockId) != null) { - connect(customR.id.lockscreen_clock_view_large, BOTTOM, lockId, TOP) - } else { - // Copied calculation codes from applyConstraints in DefaultDeviceEntrySection - val bottomPaddingPx = - context.resources.getDimensionPixelSize(R.dimen.lock_icon_margin_bottom) - val defaultDensity = - DisplayMetrics.DENSITY_DEVICE_STABLE.toFloat() / - DisplayMetrics.DENSITY_DEFAULT.toFloat() - val lockIconRadiusPx = (defaultDensity * 36).toInt() - val clockBottomMargin = bottomPaddingPx + 2 * lockIconRadiusPx - connect( - customR.id.lockscreen_clock_view_large, - BOTTOM, - PARENT_ID, - BOTTOM, - clockBottomMargin, - ) - } - - constrainWidth(customR.id.lockscreen_clock_view, WRAP_CONTENT) - constrainHeight( - customR.id.lockscreen_clock_view, - context.resources.getDimensionPixelSize(customR.dimen.small_clock_height), - ) - connect( - customR.id.lockscreen_clock_view, - START, - PARENT_ID, - START, - context.resources.getDimensionPixelSize(customR.dimen.clock_padding_start) + - context.resources.getDimensionPixelSize(R.dimen.status_view_margin_horizontal), - ) - val smallClockTopMargin = - context.resources.getDimensionPixelSize(R.dimen.keyguard_clock_top_margin) + - Utils.getStatusBarHeaderHeightKeyguard(context) - connect(customR.id.lockscreen_clock_view, TOP, PARENT_ID, TOP, smallClockTopMargin) - } - } - private fun applyPreviewConstraints( context: Context, rootView: ConstraintLayout, @@ -210,9 +127,8 @@ object KeyguardPreviewClockViewBinder { viewModel: KeyguardPreviewClockViewModel, ) { val cs = ConstraintSet().apply { clone(rootView) } - applyClockDefaultConstraints(context, cs) - previewClock.largeClock.layout.applyPreviewConstraints(cs) - previewClock.smallClock.layout.applyPreviewConstraints(cs) + previewClock.largeClock.layout.applyPreviewConstraints(context, cs) + previewClock.smallClock.layout.applyPreviewConstraints(context, cs) // When selectedClockSize is the initial value, make both clocks invisible to avoid // flickering diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt index ee4f41ddd5a0..6096cf74a772 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt @@ -186,12 +186,23 @@ constructor( constraints.apply { connect(customR.id.lockscreen_clock_view_large, START, PARENT_ID, START) connect(customR.id.lockscreen_clock_view_large, END, guideline, END) - connect(customR.id.lockscreen_clock_view_large, BOTTOM, R.id.device_entry_icon_view, TOP) + connect( + customR.id.lockscreen_clock_view_large, + BOTTOM, + R.id.device_entry_icon_view, + TOP, + ) val largeClockTopMargin = keyguardClockViewModel.getLargeClockTopMargin() + getDimen(DATE_WEATHER_VIEW_HEIGHT) + getDimen(ENHANCED_SMARTSPACE_HEIGHT) - connect(customR.id.lockscreen_clock_view_large, TOP, PARENT_ID, TOP, largeClockTopMargin) + connect( + customR.id.lockscreen_clock_view_large, + TOP, + PARENT_ID, + TOP, + largeClockTopMargin, + ) constrainWidth(customR.id.lockscreen_clock_view_large, WRAP_CONTENT) // The following two lines make lockscreen_clock_view_large is constrained to available diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt index c11005d38986..a595d815e016 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt @@ -93,18 +93,18 @@ class ClockSizeTransition( fromBounds: Rect, toBounds: Rect, fromSSBounds: Rect?, - toSSBounds: Rect? + toSSBounds: Rect?, ) {} override fun createAnimator( sceenRoot: ViewGroup, startValues: TransitionValues?, - endValues: TransitionValues? + endValues: TransitionValues?, ): Animator? { if (startValues == null || endValues == null) { Log.w( TAG, - "Couldn't create animator: startValues=$startValues; endValues=$endValues" + "Couldn't create animator: startValues=$startValues; endValues=$endValues", ) return null } @@ -137,7 +137,7 @@ class ClockSizeTransition( "Skipping no-op transition: $toView; " + "vis: $fromVis -> $toVis; " + "alpha: $fromAlpha -> $toAlpha; " + - "bounds: $fromBounds -> $toBounds; " + "bounds: $fromBounds -> $toBounds; ", ) } return null @@ -151,7 +151,7 @@ class ClockSizeTransition( lerp(fromBounds.left, toBounds.left, fract), lerp(fromBounds.top, toBounds.top, fract), lerp(fromBounds.right, toBounds.right, fract), - lerp(fromBounds.bottom, toBounds.bottom, fract) + lerp(fromBounds.bottom, toBounds.bottom, fract), ) fun assignAnimValues(src: String, fract: Float, vis: Int? = null) { @@ -160,7 +160,7 @@ class ClockSizeTransition( if (DEBUG) { Log.i( TAG, - "$src: $toView; fract=$fract; alpha=$alpha; vis=$vis; bounds=$bounds;" + "$src: $toView; fract=$fract; alpha=$alpha; vis=$vis; bounds=$bounds;", ) } toView.setVisibility(vis ?: View.VISIBLE) @@ -174,7 +174,7 @@ class ClockSizeTransition( "transitioning: $toView; " + "vis: $fromVis -> $toVis; " + "alpha: $fromAlpha -> $toAlpha; " + - "bounds: $fromBounds -> $toBounds; " + "bounds: $fromBounds -> $toBounds; ", ) } @@ -258,7 +258,7 @@ class ClockSizeTransition( fromBounds: Rect, toBounds: Rect, fromSSBounds: Rect?, - toSSBounds: Rect? + toSSBounds: Rect?, ) { // Move normally if clock is not changing visibility if (fromIsVis == toIsVis) return @@ -347,12 +347,17 @@ class ClockSizeTransition( fromBounds: Rect, toBounds: Rect, fromSSBounds: Rect?, - toSSBounds: Rect? + toSSBounds: Rect?, ) { // If view is changing visibility, hold it in place if (fromIsVis == toIsVis) return if (DEBUG) Log.i(TAG, "Holding position of ${view.id}") - toBounds.set(fromBounds) + + if (fromIsVis) { + toBounds.set(fromBounds) + } else { + fromBounds.set(toBounds) + } } companion object { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt index 5c79c0b5c1bb..3a7a640be85f 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt @@ -181,12 +181,15 @@ constructor( fun getLargeClockTopMargin(): Int { return systemBarUtils.getStatusBarHeight() + resources.getDimensionPixelSize(customR.dimen.small_clock_padding_top) + - resources.getDimensionPixelSize(R.dimen.keyguard_smartspace_top_offset) + resources.getDimensionPixelSize(customR.dimen.keyguard_smartspace_top_offset) } val largeClockTopMargin: Flow<Int> = configurationInteractor.onAnyConfigurationChange.map { getLargeClockTopMargin() } + val largeClockTextSize: Flow<Int> = + configurationInteractor.dimensionPixelSize(customR.dimen.large_clock_text_size) + enum class ClockLayout { LARGE_CLOCK, SMALL_CLOCK, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardPreviewSmartspaceViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardPreviewSmartspaceViewModel.kt index 6579ea162ee2..65c0f57b76f5 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardPreviewSmartspaceViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardPreviewSmartspaceViewModel.kt @@ -18,6 +18,7 @@ package com.android.systemui.keyguard.ui.viewmodel import android.content.Context import com.android.internal.policy.SystemBarUtils +import com.android.systemui.customization.R as customR import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor import com.android.systemui.keyguard.shared.model.ClockSizeSetting import com.android.systemui.res.R @@ -39,20 +40,16 @@ constructor( val selectedClockSize: StateFlow<ClockSizeSetting> = interactor.selectedClockSize val shouldHideSmartspace: Flow<Boolean> = - combine( - interactor.selectedClockSize, - interactor.currentClockId, - ::Pair, - ) - .map { (size, currentClockId) -> - when (size) { - // TODO (b/284122375) This is temporary. We should use clockController - // .largeClock.config.hasCustomWeatherDataDisplay instead, but - // ClockRegistry.createCurrentClock is not reliable. - ClockSizeSetting.DYNAMIC -> currentClockId == "DIGITAL_CLOCK_WEATHER" - ClockSizeSetting.SMALL -> false - } + combine(interactor.selectedClockSize, interactor.currentClockId, ::Pair).map { + (size, currentClockId) -> + when (size) { + // TODO (b/284122375) This is temporary. We should use clockController + // .largeClock.config.hasCustomWeatherDataDisplay instead, but + // ClockRegistry.createCurrentClock is not reliable. + ClockSizeSetting.DYNAMIC -> currentClockId == "DIGITAL_CLOCK_WEATHER" + ClockSizeSetting.SMALL -> false } + } fun getSmartspaceStartPadding(context: Context): Int { return KeyguardSmartspaceViewModel.getSmartspaceStartMargin(context) @@ -83,7 +80,7 @@ constructor( } else { getDimensionPixelSize(R.dimen.keyguard_clock_top_margin) + SystemBarUtils.getStatusBarHeight(context) + - getDimensionPixelSize(R.dimen.keyguard_smartspace_top_offset) + getDimensionPixelSize(customR.dimen.keyguard_smartspace_top_offset) } } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt index 850e943d17eb..ef6ae0dd6427 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt @@ -17,8 +17,10 @@ package com.android.systemui.keyguard.ui.viewmodel import android.content.res.Resources +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.internal.annotations.VisibleForTesting import com.android.systemui.biometrics.AuthController +import com.android.systemui.customization.R as customR import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor @@ -42,7 +44,6 @@ import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn -import com.android.app.tracing.coroutines.launchTraced as launch class LockscreenContentViewModel @AssistedInject @@ -82,10 +83,7 @@ constructor( unfoldTransitionInteractor.unfoldTranslationX(isOnStartSide = true), unfoldTransitionInteractor.unfoldTranslationX(isOnStartSide = false), ) { start, end -> - UnfoldTranslations( - start = start, - end = end, - ) + UnfoldTranslations(start = start, end = end) } .collect { _unfoldTranslations.value = it } } @@ -102,17 +100,15 @@ constructor( /** Returns a flow that indicates whether lockscreen notifications should be rendered. */ fun areNotificationsVisible(): Flow<Boolean> { - return combine( - clockSize, - shadeInteractor.isShadeLayoutWide, - ) { clockSize, isShadeLayoutWide -> + return combine(clockSize, shadeInteractor.isShadeLayoutWide) { clockSize, isShadeLayoutWide + -> clockSize == ClockSize.SMALL || isShadeLayoutWide } } fun getSmartSpacePaddingTop(resources: Resources): Int { return if (clockSize.value == ClockSize.LARGE) { - resources.getDimensionPixelSize(R.dimen.keyguard_smartspace_top_offset) + + resources.getDimensionPixelSize(customR.dimen.keyguard_smartspace_top_offset) + resources.getDimensionPixelSize(R.dimen.keyguard_clock_top_margin) } else { 0 diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/Media3ActionFactory.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/Media3ActionFactory.kt index d38e507082b9..913aa6f9d547 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/Media3ActionFactory.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/Media3ActionFactory.kt @@ -42,7 +42,7 @@ import com.android.systemui.media.controls.shared.model.MediaButton import com.android.systemui.media.controls.util.MediaControllerFactory import com.android.systemui.media.controls.util.SessionTokenFactory import com.android.systemui.res.R -import com.android.systemui.util.Assert +import com.android.systemui.util.concurrency.Execution import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch @@ -63,6 +63,7 @@ constructor( @Background private val looper: Looper, @Background private val handler: Handler, @Background private val bgScope: CoroutineScope, + private val execution: Execution, ) { /** @@ -108,7 +109,7 @@ constructor( m3controller: Media3Controller, token: SessionToken, ): MediaButton? { - Assert.isNotMainThread() + require(!execution.isMainThread()) // First, get standard actions val playOrPause = diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java index f49693a8700b..80ac2fcd4aa4 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java @@ -696,7 +696,8 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack TaskStackChangeListeners.getInstance().unregisterTaskStackListener( mTaskStackListener); DeviceConfig.removeOnPropertiesChangedListener(mOnPropertiesChangedListener); - mPipOptional.ifPresent(pip -> pip.setOnIsInPipStateChangedListener(null)); + mPipOptional.ifPresent(pip -> pip.removeOnIsInPipStateChangedListener( + mOnIsInPipStateChangedListener)); try { mWindowManagerService.unregisterSystemGestureExclusionListener( @@ -720,7 +721,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack mTaskStackListener); DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI, mUiThreadContext.getExecutor()::execute, mOnPropertiesChangedListener); - mPipOptional.ifPresent(pip -> pip.setOnIsInPipStateChangedListener( + mPipOptional.ifPresent(pip -> pip.addOnIsInPipStateChangedListener( mOnIsInPipStateChangedListener)); mDesktopModeOptional.ifPresent( dm -> dm.addDesktopGestureExclusionRegionListener( diff --git a/packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt b/packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt index 8d48c1d1d23f..1cf4c23415da 100644 --- a/packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt @@ -26,11 +26,13 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.power.data.repository.PowerRepository import com.android.systemui.power.shared.model.ScreenPowerState import com.android.systemui.power.shared.model.WakeSleepReason +import com.android.systemui.power.shared.model.WakefulnessModel import com.android.systemui.power.shared.model.WakefulnessState import com.android.systemui.statusbar.phone.ScreenOffAnimationController import javax.inject.Inject import javax.inject.Provider import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map @@ -56,7 +58,7 @@ constructor( * Unless you need to respond differently to different [WakeSleepReason]s, you should use * [isAwake]. */ - val detailedWakefulness = repository.wakefulness + val detailedWakefulness: StateFlow<WakefulnessModel> = repository.wakefulness /** * Whether we're awake (screen is on and responding to user touch) or asleep (screen is off, or @@ -189,9 +191,7 @@ constructor( * In tests, you should be able to use [setAsleepForTest] rather than calling this method * directly. */ - fun onFinishedGoingToSleep( - powerButtonLaunchGestureTriggeredDuringSleep: Boolean, - ) { + fun onFinishedGoingToSleep(powerButtonLaunchGestureTriggeredDuringSleep: Boolean) { // If the launch gesture was previously detected via onCameraLaunchGestureDetected, carry // that state forward. It will be reset by the next onStartedGoingToSleep. val powerButtonLaunchGestureTriggered = @@ -255,7 +255,7 @@ constructor( @JvmOverloads fun PowerInteractor.setAwakeForTest( @PowerManager.WakeReason reason: Int = PowerManager.WAKE_REASON_UNKNOWN, - forceEmit: Boolean = false + forceEmit: Boolean = false, ) { emitDuplicateWakefulnessValue = forceEmit @@ -286,9 +286,7 @@ constructor( emitDuplicateWakefulnessValue = forceEmit this.onStartedGoingToSleep(reason = sleepReason) - this.onFinishedGoingToSleep( - powerButtonLaunchGestureTriggeredDuringSleep = false, - ) + this.onFinishedGoingToSleep(powerButtonLaunchGestureTriggeredDuringSleep = false) } /** Helper method for tests to simulate the device screen state change event. */ diff --git a/packages/SystemUI/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerController.java b/packages/SystemUI/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerController.java index 765b45bdbf2e..bab88c0b0bf9 100644 --- a/packages/SystemUI/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerController.java +++ b/packages/SystemUI/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerController.java @@ -159,7 +159,7 @@ public class QRCodeScannerController implements * Returns true if lock screen entry point for QR Code Scanner is to be enabled. */ public boolean isEnabledForLockScreenButton() { - return mQRCodeScannerEnabled && isAbleToLaunchScannerActivity() && isAllowedOnLockScreen(); + return isAbleToLaunchScannerActivity() && isAllowedOnLockScreen(); } /** Returns whether the QR scanner button is allowed on lockscreen. */ 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 51ff598c580b..ec6a17b24989 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt @@ -70,6 +70,7 @@ import androidx.compose.ui.layout.approachLayout import androidx.compose.ui.layout.onPlaced import androidx.compose.ui.layout.onSizeChanged import androidx.compose.ui.layout.positionInRoot +import androidx.compose.ui.layout.positionOnScreen import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.stringResource @@ -124,6 +125,7 @@ import com.android.systemui.qs.ui.composable.QuickSettingsShade import com.android.systemui.qs.ui.composable.QuickSettingsTheme import com.android.systemui.res.R import com.android.systemui.util.LifecycleFragment +import com.android.systemui.util.animation.UniqueObjectHostView import com.android.systemui.util.asIndenting import com.android.systemui.util.printSection import com.android.systemui.util.println @@ -447,8 +449,7 @@ constructor( } override fun setShouldUpdateSquishinessOnMedia(shouldUpdate: Boolean) { - super.setShouldUpdateSquishinessOnMedia(shouldUpdate) - // TODO (b/353253280) + viewModel.shouldUpdateSquishinessOnMedia = shouldUpdate } override fun setInSplitShade(isInSplitShade: Boolean) { @@ -660,7 +661,20 @@ constructor( Column( modifier = - Modifier.offset { + Modifier.onPlaced { coordinates -> + val positionOnScreen = coordinates.positionOnScreen() + val left = positionOnScreen.x + val right = left + coordinates.size.width + val top = positionOnScreen.y + val bottom = top + coordinates.size.height + viewModel.applyNewQsScrollerBounds( + left = left, + top = top, + right = right, + bottom = bottom, + ) + } + .offset { IntOffset( x = 0, y = viewModel.qsScrollTranslationY.fastRoundToInt(), @@ -704,7 +718,10 @@ constructor( val Media = @Composable { if (viewModel.qsMediaVisible) { - MediaObject(mediaHost = viewModel.qsMediaHost) + MediaObject( + mediaHost = viewModel.qsMediaHost, + update = { translationY = viewModel.qsMediaTranslationY }, + ) } } Box( @@ -987,7 +1004,11 @@ private fun Modifier.gesturesDisabled(disabled: Boolean) = } @Composable -private fun MediaObject(mediaHost: MediaHost, modifier: Modifier = Modifier) { +private fun MediaObject( + mediaHost: MediaHost, + modifier: Modifier = Modifier, + update: UniqueObjectHostView.() -> Unit = {}, +) { Box { AndroidView( modifier = modifier, @@ -1000,6 +1021,7 @@ private fun MediaObject(mediaHost: MediaHost, modifier: Modifier = Modifier) { ) } }, + update = { view -> view.update() }, onReset = {}, ) } diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt index 624cf306a3b2..9029563b6321 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt @@ -26,6 +26,7 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.compose.runtime.snapshotFlow import androidx.lifecycle.LifecycleCoroutineScope +import com.android.app.animation.Interpolators import com.android.app.tracing.coroutines.launchTraced as launch import com.android.keyguard.BouncerPanelExpansionCalculator import com.android.systemui.Dumpable @@ -63,6 +64,7 @@ import com.android.systemui.statusbar.SysuiStatusBarStateController import com.android.systemui.statusbar.disableflags.data.repository.DisableFlagsRepository import com.android.systemui.util.LargeScreenUtils import com.android.systemui.util.asIndenting +import com.android.systemui.util.kotlin.emitOnStart import com.android.systemui.util.printSection import com.android.systemui.util.println import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow @@ -269,6 +271,23 @@ constructor( val qsMediaInRow: Boolean get() = qsMediaInRowViewModel.shouldMediaShowInRow + var shouldUpdateSquishinessOnMedia by mutableStateOf(false) + + val qsMediaTranslationY by derivedStateOf { + if ( + qsExpansion > 0f && + !isKeyguardState && + !qqsMediaVisible && + !qsMediaInRow && + !isInSplitShade + ) { + val interpolation = Interpolators.ACCELERATE.getInterpolation(1f - qsExpansion) + -qsMediaHost.hostView.height * 1.3f * interpolation + } else { + 0f + } + } + val animateTilesExpansion: Boolean get() = inFirstPage && !mediaSuddenlyAppearingInLandscape @@ -278,6 +297,36 @@ constructor( private val mediaSuddenlyAppearingInLandscape: Boolean get() = !qqsMediaInRow && qsMediaInRow + private val collapsedLandscapeMedia by + hydrator.hydratedStateOf( + traceName = "collapsedLandscapeMedia", + initialValue = resources.getBoolean(R.bool.config_quickSettingsMediaLandscapeCollapsed), + source = + configurationInteractor.onAnyConfigurationChange.emitOnStart().map { + resources.getBoolean(R.bool.config_quickSettingsMediaLandscapeCollapsed) + }, + ) + + private val qqsMediaExpansion: Float + get() = + if (qqsMediaInRow && collapsedLandscapeMedia) { + MediaHostState.COLLAPSED + } else { + MediaHostState.EXPANDED + } + + private val shouldApplySquishinessToMedia by derivedStateOf { + shouldUpdateSquishinessOnMedia || (isInSplitShade && statusBarState == StatusBarState.SHADE) + } + + private val mediaSquishiness by derivedStateOf { + if (shouldApplySquishinessToMedia) { + squishinessFraction + } else { + 1f + } + } + private var qsBounds by mutableStateOf(Rect()) private val constrainedSquishinessFraction: Float @@ -336,8 +385,6 @@ constructor( private val isOverscrolling: Boolean get() = overScrollAmount != 0 - private var shouldUpdateMediaSquishiness by mutableStateOf(false) - private val forceQs by derivedStateOf { (isQsExpanded || isStackScrollerOverscrolling) && (isKeyguardState && !showCollapsedOnKeyguard) @@ -375,10 +422,26 @@ constructor( ), ) + fun applyNewQsScrollerBounds(left: Float, top: Float, right: Float, bottom: Float) { + if (usingMedia) { + qsMediaHost.currentClipping.set( + left.toInt(), + top.toInt(), + right.toInt(), + bottom.toInt(), + ) + } + } + override suspend fun onActivated(): Nothing { initMediaHosts() // init regardless of using media (same as current QS). coroutineScope { launch { hydrateSquishinessInteractor() } + if (usingMedia) { + launch { hydrateQqsMediaExpansion() } + launch { hydrateMediaSquishiness() } + launch { hydrateMediaDisappearParameters() } + } launch { hydrator.activate() } launch { containerViewModel.activate() } launch { qqsMediaInRowViewModel.activate() } @@ -389,7 +452,7 @@ constructor( private fun initMediaHosts() { qqsMediaHost.apply { - expansion = MediaHostState.EXPANDED + expansion = qqsMediaExpansion showsOnlyActiveMedia = true init(MediaHierarchyManager.LOCATION_QQS) } @@ -405,6 +468,25 @@ constructor( .collect { squishinessInteractor.setSquishinessValue(it) } } + private suspend fun hydrateQqsMediaExpansion() { + snapshotFlow { qqsMediaExpansion }.collect { qqsMediaHost.expansion = it } + } + + private suspend fun hydrateMediaSquishiness() { + snapshotFlow { mediaSquishiness }.collect { qsMediaHost.squishFraction = it } + } + + private suspend fun hydrateMediaDisappearParameters() { + coroutineScope { + launch { + snapshotFlow { qqsMediaInRow }.collect { qqsMediaHost.applyDisappearParameters(it) } + } + launch { + snapshotFlow { qsMediaInRow }.collect { qsMediaHost.applyDisappearParameters(it) } + } + } + } + override fun dump(pw: PrintWriter, args: Array<out String>) { pw.asIndenting().run { printSection("Quick Settings state") { @@ -448,6 +530,11 @@ constructor( println("qqsMediaInRow", qqsMediaInRow) println("qsMediaVisible", qsMediaVisible) println("qsMediaInRow", qsMediaInRow) + println("collapsedLandscapeMedia", collapsedLandscapeMedia) + println("qqsMediaExpansion", qqsMediaExpansion) + println("shouldUpdateSquishinessOnMedia", shouldUpdateSquishinessOnMedia) + println("mediaSquishiness", mediaSquishiness) + println("qsMediaTranslationY", qsMediaTranslationY) } } } @@ -484,3 +571,22 @@ private fun mediaHostVisible(mediaHost: MediaHost): Flow<Boolean> { // lazily. .onStart { emit(mediaHost.visible) } } + +// Taken from QSPanelControllerBase +private fun MediaHost.applyDisappearParameters(inRow: Boolean) { + disappearParameters.apply { + fadeStartPosition = 0.95f + disappearStart = 0f + if (inRow) { + disappearSize.set(0f, 0.4f) + gonePivot.set(1f, 0f) + contentTranslationFraction.set(0.25f, 1f) + disappearEnd = 0.6f + } else { + disappearSize.set(1f, 0f) + gonePivot.set(0f, 0f) + contentTranslationFraction.set(0f, 1f) + disappearEnd = 0.95f + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/LargeTileSpanRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/LargeTileSpanRepository.kt new file mode 100644 index 000000000000..58834037e2b7 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/LargeTileSpanRepository.kt @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.panels.data.repository + +import android.content.res.Resources +import com.android.systemui.common.ui.data.repository.ConfigurationRepository +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.res.R +import com.android.systemui.util.kotlin.emitOnStart +import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.mapLatest +import kotlinx.coroutines.flow.stateIn + +@OptIn(ExperimentalCoroutinesApi::class) +@SysUISingleton +class LargeTileSpanRepository +@Inject +constructor( + @Application scope: CoroutineScope, + @Main private val resources: Resources, + configurationRepository: ConfigurationRepository, +) { + val span: StateFlow<Int> = + configurationRepository.onConfigurationChange + .emitOnStart() + .mapLatest { + if (resources.configuration.fontScale >= FONT_SCALE_THRESHOLD) { + resources.getInteger(R.integer.quick_settings_infinite_grid_tile_max_width) + } else { + 2 + } + } + .distinctUntilChanged() + .stateIn(scope, SharingStarted.WhileSubscribed(), 2) + + private companion object { + const val FONT_SCALE_THRESHOLD = 2f + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QSPreferencesRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QSPreferencesRepository.kt index 971598dea0bd..b0c607303297 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QSPreferencesRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QSPreferencesRepository.kt @@ -17,9 +17,16 @@ package com.android.systemui.qs.panels.data.repository import android.content.Context +import android.content.IntentFilter import android.content.SharedPreferences +import com.android.systemui.backup.BackupHelper +import com.android.systemui.backup.BackupHelper.Companion.ACTION_RESTORE_FINISHED +import com.android.systemui.broadcast.BroadcastDispatcher 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.qs.panels.shared.model.PanelsLog import com.android.systemui.qs.pipeline.shared.TileSpec import com.android.systemui.settings.UserFileManager import com.android.systemui.user.data.repository.UserRepository @@ -29,9 +36,11 @@ import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onEach /** Repository for QS user preferences. */ @OptIn(ExperimentalCoroutinesApi::class) @@ -43,26 +52,31 @@ constructor( private val userRepository: UserRepository, private val defaultLargeTilesRepository: DefaultLargeTilesRepository, @Background private val backgroundDispatcher: CoroutineDispatcher, + @PanelsLog private val logBuffer: LogBuffer, + broadcastDispatcher: BroadcastDispatcher, ) { - /** Whether to show the labels on icon tiles for the current user. */ - val showLabels: Flow<Boolean> = - userRepository.selectedUserInfo - .flatMapLatest { userInfo -> - val prefs = getSharedPrefs(userInfo.id) - prefs.observe().emitOnStart().map { prefs.getBoolean(ICON_LABELS_KEY, false) } - } - .flowOn(backgroundDispatcher) + private val logger by lazy { Logger(logBuffer, TAG) } + + private val backupRestorationEvents: Flow<Unit> = + broadcastDispatcher + .broadcastFlow( + filter = IntentFilter(ACTION_RESTORE_FINISHED), + flags = Context.RECEIVER_NOT_EXPORTED, + permission = BackupHelper.PERMISSION_SELF, + ) + .onEach { logger.i("Restored state for QS preferences.") } + .emitOnStart() /** Set of [TileSpec] to display as large tiles for the current user. */ val largeTilesSpecs: Flow<Set<TileSpec>> = - userRepository.selectedUserInfo - .flatMapLatest { userInfo -> + combine(backupRestorationEvents, userRepository.selectedUserInfo, ::Pair) + .flatMapLatest { (_, userInfo) -> val prefs = getSharedPrefs(userInfo.id) prefs.observe().emitOnStart().map { prefs .getStringSet( LARGE_TILES_SPECS_KEY, - defaultLargeTilesRepository.defaultLargeTiles.map { it.spec }.toSet() + defaultLargeTilesRepository.defaultLargeTiles.map { it.spec }.toSet(), ) ?.map { TileSpec.create(it) } ?.toSet() ?: defaultLargeTilesRepository.defaultLargeTiles @@ -70,13 +84,6 @@ constructor( } .flowOn(backgroundDispatcher) - /** Sets for the current user whether to show the labels on icon tiles. */ - fun setShowLabels(showLabels: Boolean) { - with(getSharedPrefs(userRepository.getSelectedUserInfo().id)) { - edit().putBoolean(ICON_LABELS_KEY, showLabels).apply() - } - } - /** Sets for the current user the set of [TileSpec] to display as large tiles. */ fun setLargeTilesSpecs(specs: Set<TileSpec>) { with(getSharedPrefs(userRepository.getSelectedUserInfo().id)) { @@ -85,15 +92,11 @@ constructor( } private fun getSharedPrefs(userId: Int): SharedPreferences { - return userFileManager.getSharedPreferences( - FILE_NAME, - Context.MODE_PRIVATE, - userId, - ) + return userFileManager.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE, userId) } companion object { - private const val ICON_LABELS_KEY = "show_icon_labels" + private const val TAG = "QSPreferencesRepository" private const val LARGE_TILES_SPECS_KEY = "large_tiles_specs" const val FILE_NAME = "quick_settings_prefs" } diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/backup/QSPreferencesBackupHelper.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/backup/QSPreferencesBackupHelper.kt new file mode 100644 index 000000000000..2c51ca9c253f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/backup/QSPreferencesBackupHelper.kt @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.panels.domain.backup + +import android.app.backup.SharedPreferencesBackupHelper +import android.content.Context +import com.android.systemui.qs.panels.data.repository.QSPreferencesRepository.Companion.FILE_NAME +import com.android.systemui.settings.UserFileManagerImpl + +class QSPreferencesBackupHelper(context: Context, userId: Int) : + SharedPreferencesBackupHelper( + context, + UserFileManagerImpl.createFile(userId = userId, fileName = FILE_NAME).path, + ) diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractor.kt index ec61a0d5769e..23c79f576df5 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractor.kt @@ -21,12 +21,14 @@ import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.log.LogBuffer import com.android.systemui.log.core.LogLevel import com.android.systemui.qs.panels.data.repository.DefaultLargeTilesRepository +import com.android.systemui.qs.panels.data.repository.LargeTileSpanRepository import com.android.systemui.qs.panels.shared.model.PanelsLog import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor import com.android.systemui.qs.pipeline.shared.TileSpec import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.stateIn @@ -38,6 +40,7 @@ constructor( private val repo: DefaultLargeTilesRepository, private val currentTilesInteractor: CurrentTilesInteractor, private val preferencesInteractor: QSPreferencesInteractor, + largeTilesSpanRepo: LargeTileSpanRepository, @PanelsLog private val logBuffer: LogBuffer, @Application private val applicationScope: CoroutineScope, ) { @@ -46,6 +49,8 @@ constructor( .onEach { logChange(it) } .stateIn(applicationScope, SharingStarted.Eagerly, repo.defaultLargeTiles) + val largeTilesSpan: StateFlow<Int> = largeTilesSpanRepo.span + fun isIconTile(spec: TileSpec): Boolean = !largeTilesSpecs.value.contains(spec) fun setLargeTiles(specs: Set<TileSpec>) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditTileListState.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditTileListState.kt index 74fa0fef21d7..c729c7c15176 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditTileListState.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditTileListState.kt @@ -37,13 +37,17 @@ import com.android.systemui.qs.pipeline.shared.TileSpec fun rememberEditListState( tiles: List<SizedTile<EditTileViewModel>>, columns: Int, + largeTilesSpan: Int, ): EditTileListState { - return remember(tiles, columns) { EditTileListState(tiles, columns) } + return remember(tiles, columns) { EditTileListState(tiles, columns, largeTilesSpan) } } /** Holds the temporary state of the tile list during a drag movement where we move tiles around. */ -class EditTileListState(tiles: List<SizedTile<EditTileViewModel>>, private val columns: Int) : - DragAndDropState { +class EditTileListState( + tiles: List<SizedTile<EditTileViewModel>>, + private val columns: Int, + private val largeTilesSpan: Int, +) : DragAndDropState { private val _draggedCell = mutableStateOf<SizedTile<EditTileViewModel>?>(null) override val draggedCell get() = _draggedCell.value @@ -86,7 +90,7 @@ class EditTileListState(tiles: List<SizedTile<EditTileViewModel>>, private val c if (fromIndex != -1) { val cell = _tiles.removeAt(fromIndex) cell as TileGridCell - _tiles.add(fromIndex, cell.copy(width = if (cell.isIcon) 2 else 1)) + _tiles.add(fromIndex, cell.copy(width = if (cell.isIcon) largeTilesSpan else 1)) regenerateGrid(fromIndex) } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt index d10722287f5d..177a5be35592 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt @@ -17,6 +17,7 @@ package com.android.systemui.qs.panels.ui.compose.infinitegrid import android.graphics.drawable.Animatable +import android.graphics.drawable.Drawable import android.text.TextUtils import androidx.compose.animation.animateColorAsState import androidx.compose.animation.graphics.ExperimentalAnimationGraphicsApi @@ -30,10 +31,11 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.BasicText -import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -63,11 +65,14 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import com.android.compose.modifiers.size import com.android.compose.modifiers.thenIf +import com.android.compose.ui.graphics.painter.rememberDrawablePainter import com.android.systemui.Flags import com.android.systemui.common.shared.model.Icon import com.android.systemui.common.ui.compose.Icon import com.android.systemui.common.ui.compose.load import com.android.systemui.compose.modifiers.sysuiResTag +import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.SideIconHeight +import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.SideIconWidth import com.android.systemui.qs.panels.ui.compose.infinitegrid.CommonTileDefaults.longPressLabel import com.android.systemui.qs.panels.ui.viewmodel.AccessibilityUiState import com.android.systemui.res.R @@ -79,6 +84,7 @@ fun LargeTileContent( label: String, secondaryLabel: String?, icon: Icon, + sideDrawable: Drawable?, colors: TileColors, squishiness: () -> Float, accessibilityUiState: AccessibilityUiState? = null, @@ -135,6 +141,14 @@ fun LargeTileContent( colors = colors, accessibilityUiState = accessibilityUiState, ) + + if (sideDrawable != null) { + Image( + painter = rememberDrawablePainter(sideDrawable), + contentDescription = null, + modifier = Modifier.width(SideIconWidth).height(SideIconHeight), + ) + } } } @@ -194,32 +208,43 @@ fun SmallTileContent( is Icon.Resource -> context.getDrawable(icon.res) } } - if (loadedDrawable !is Animatable) { - Icon(icon = icon, tint = animatedColor, modifier = iconModifier) - } else if (icon is Icon.Resource) { - val image = AnimatedImageVector.animatedVectorResource(id = icon.res) + if (loadedDrawable is Animatable) { val painter = - key(icon) { - if (animateToEnd) { - rememberAnimatedVectorPainter(animatedImageVector = image, atEnd = true) - } else { - var atEnd by remember(icon.res) { mutableStateOf(false) } - LaunchedEffect(key1 = icon.res) { atEnd = true } - rememberAnimatedVectorPainter(animatedImageVector = image, atEnd = atEnd) + when (icon) { + is Icon.Resource -> { + val image = AnimatedImageVector.animatedVectorResource(id = icon.res) + key(icon) { + if (animateToEnd) { + rememberAnimatedVectorPainter(animatedImageVector = image, atEnd = true) + } else { + var atEnd by remember(icon.res) { mutableStateOf(false) } + LaunchedEffect(key1 = icon.res) { atEnd = true } + rememberAnimatedVectorPainter( + animatedImageVector = image, + atEnd = atEnd, + ) + } + } } + is Icon.Loaded -> rememberDrawablePainter(loadedDrawable) } + Image( painter = painter, contentDescription = icon.contentDescription?.load(), colorFilter = ColorFilter.tint(color = animatedColor), modifier = iconModifier, ) + } else { + Icon(icon = icon, tint = animatedColor, modifier = iconModifier) } } object CommonTileDefaults { val IconSize = 32.dp val LargeTileIconSize = 28.dp + val SideIconWidth = 32.dp + val SideIconHeight = 20.dp val ToggleTargetSize = 56.dp val TileHeight = 72.dp val TilePadding = 8.dp diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt index b5cec120987f..31ea60e2f0bc 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt @@ -26,7 +26,7 @@ import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut import androidx.compose.foundation.ExperimentalFoundationApi -import androidx.compose.foundation.LocalOverscrollConfiguration +import androidx.compose.foundation.LocalOverscrollFactory import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.gestures.Orientation @@ -173,6 +173,7 @@ fun DefaultEditTileGrid( listState: EditTileListState, otherTiles: List<SizedTile<EditTileViewModel>>, columns: Int, + largeTilesSpan: Int, modifier: Modifier, onRemoveTile: (TileSpec) -> Unit, onSetTiles: (List<TileSpec>) -> Unit, @@ -203,7 +204,7 @@ fun DefaultEditTileGrid( containerColor = Color.Transparent, topBar = { EditModeTopBar(onStopEditing = onStopEditing, onReset = reset) }, ) { innerPadding -> - CompositionLocalProvider(LocalOverscrollConfiguration provides null) { + CompositionLocalProvider(LocalOverscrollFactory provides null) { val scrollState = rememberScrollState() LaunchedEffect(listState.dragInProgress) { if (listState.dragInProgress) { @@ -230,7 +231,14 @@ fun DefaultEditTileGrid( } } - CurrentTilesGrid(listState, selectionState, columns, onResize, onSetTiles) + CurrentTilesGrid( + listState, + selectionState, + columns, + largeTilesSpan, + onResize, + onSetTiles, + ) // Hide available tiles when dragging AnimatedVisibility( @@ -273,7 +281,7 @@ private fun EditGridHeader( ) { Box( contentAlignment = Alignment.Center, - modifier = modifier.fillMaxWidth().height(EditModeTileDefaults.EditGridHeaderHeight), + modifier = modifier.fillMaxWidth().wrapContentHeight(), ) { content() } @@ -300,6 +308,7 @@ private fun CurrentTilesGrid( listState: EditTileListState, selectionState: MutableSelectionState, columns: Int, + largeTilesSpan: Int, onResize: (TileSpec, toIcon: Boolean) -> Unit, onSetTiles: (List<TileSpec>) -> Unit, ) { @@ -340,7 +349,8 @@ private fun CurrentTilesGrid( } .testTag(CURRENT_TILES_GRID_TEST_TAG), ) { - EditTiles(cells, columns, listState, selectionState, coroutineScope) { spec -> + EditTiles(cells, columns, listState, selectionState, coroutineScope, largeTilesSpan) { spec + -> // Toggle the current size of the tile currentListState.isIcon(spec)?.let { onResize(spec, !it) } } @@ -425,6 +435,7 @@ fun LazyGridScope.EditTiles( dragAndDropState: DragAndDropState, selectionState: MutableSelectionState, coroutineScope: CoroutineScope, + largeTilesSpan: Int, onToggleSize: (spec: TileSpec) -> Unit, ) { items( @@ -456,6 +467,7 @@ fun LazyGridScope.EditTiles( onToggleSize = onToggleSize, coroutineScope = coroutineScope, bounceableInfo = cells.bounceableInfo(index, columns), + largeTilesSpan = largeTilesSpan, modifier = Modifier.animateItem(), ) } @@ -472,6 +484,7 @@ private fun TileGridCell( selectionState: MutableSelectionState, onToggleSize: (spec: TileSpec) -> Unit, coroutineScope: CoroutineScope, + largeTilesSpan: Int, bounceableInfo: BounceableInfo, modifier: Modifier = Modifier, ) { @@ -514,8 +527,11 @@ private fun TileGridCell( .fillMaxWidth() .onSizeChanged { // Grab the size before the bounceable to get the idle width - val min = if (cell.isIcon) it.width else (it.width - padding) / 2 - val max = if (cell.isIcon) (it.width * 2) + padding else it.width + val totalPadding = (largeTilesSpan - 1) * padding + val min = + if (cell.isIcon) it.width else (it.width - totalPadding) / largeTilesSpan + val max = + if (cell.isIcon) (it.width * largeTilesSpan) + totalPadding else it.width tileWidths = TileWidths(it.width, min, max) } .bounceable( @@ -554,15 +570,13 @@ private fun TileGridCell( val targetValue = if (cell.isIcon) 0f else 1f val animatedProgress = remember { Animatable(targetValue) } - if (selected) { - val resizingState = selectionState.resizingState - LaunchedEffect(targetValue, resizingState) { - if (resizingState == null) { - animatedProgress.animateTo(targetValue) - } else { - snapshotFlow { resizingState.progression } - .collectLatest { animatedProgress.snapTo(it) } - } + val resizingState = selectionState.resizingState?.takeIf { selected } + LaunchedEffect(targetValue, resizingState) { + if (resizingState == null) { + animatedProgress.animateTo(targetValue) + } else { + snapshotFlow { resizingState.progression } + .collectLatest { animatedProgress.snapTo(it) } } } @@ -705,7 +719,6 @@ private fun Modifier.tileBackground(color: Color): Modifier { private object EditModeTileDefaults { const val PLACEHOLDER_ALPHA = .3f - val EditGridHeaderHeight = 60.dp val CurrentTilesGridPadding = 8.dp @Composable diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt index 5ac2ad02d671..29ff1715dea2 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 @@ -79,7 +79,8 @@ constructor( } val columns = columnsWithMediaViewModel.columns - val sizedTiles = tiles.map { SizedTileImpl(it, it.spec.width()) } + val largeTilesSpan by iconTilesViewModel.largeTilesSpanState + val sizedTiles = tiles.map { SizedTileImpl(it, it.spec.width(largeTilesSpan)) } val bounceables = remember(sizedTiles) { List(sizedTiles.size) { BounceableTileViewModel() } } val squishiness by viewModel.squishinessViewModel.squishiness.collectAsStateWithLifecycle() @@ -129,21 +130,23 @@ constructor( viewModel.columnsWithMediaViewModelFactory.createWithoutMediaTracking() } val columns = columnsViewModel.columns + val largeTilesSpan by iconTilesViewModel.largeTilesSpanState val largeTiles by iconTilesViewModel.largeTiles.collectAsStateWithLifecycle() // Non-current tiles should always be displayed as icon tiles. val sizedTiles = - remember(tiles, largeTiles) { + remember(tiles, largeTiles, largeTilesSpan) { tiles.map { SizedTileImpl( it, - if (!it.isCurrent || !largeTiles.contains(it.tileSpec)) 1 else 2, + if (!it.isCurrent || !largeTiles.contains(it.tileSpec)) 1 + else largeTilesSpan, ) } } val (currentTiles, otherTiles) = sizedTiles.partition { it.tile.isCurrent } - val currentListState = rememberEditListState(currentTiles, columns) + val currentListState = rememberEditListState(currentTiles, columns, largeTilesSpan) DefaultEditTileGrid( listState = currentListState, otherTiles = otherTiles, @@ -154,6 +157,7 @@ constructor( onResize = iconTilesViewModel::resize, onStopEditing = onStopEditing, onReset = viewModel::showResetDialog, + largeTilesSpan = largeTilesSpan, ) } @@ -171,7 +175,7 @@ constructor( .map { it.flatten().map { it.tile } } } - private fun TileSpec.width(): Int { - return if (iconTilesViewModel.isIconTile(this)) 1 else 2 + private fun TileSpec.width(largeSize: Int = iconTilesViewModel.largeTilesSpan.value): Int { + return if (iconTilesViewModel.isIconTile(this)) 1 else largeSize } } 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 5bebdbc7a13e..fe59c4d36edc 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 @@ -130,15 +130,7 @@ fun Tile( // TODO(b/361789146): Draw the shapes instead of clipping val tileShape = TileDefaults.animateTileShape(uiState.state) - val animatedColor by - animateColorAsState( - if (iconOnly || !uiState.handlesSecondaryClick) { - colors.iconBackground - } else { - colors.background - }, - label = "QSTileBackgroundColor", - ) + val animatedColor by animateColorAsState(colors.background, label = "QSTileBackgroundColor") TileExpandable( color = { animatedColor }, @@ -156,6 +148,14 @@ fun Tile( bounceEnd = currentBounceableInfo.bounceEnd, ), ) { expandable -> + val longClick: (() -> Unit)? = + { + hapticsViewModel?.setTileInteractionState( + TileHapticsViewModel.TileInteractionState.LONG_CLICKED + ) + tile.onLongClick(expandable) + } + .takeIf { uiState.handlesLongClick } TileContainer( onClick = { tile.onClick(expandable) @@ -166,12 +166,7 @@ fun Tile( coroutineScope.launch { currentBounceableInfo.bounceable.animateBounce() } } }, - onLongClick = { - hapticsViewModel?.setTileInteractionState( - TileHapticsViewModel.TileInteractionState.LONG_CLICKED - ) - tile.onLongClick(expandable) - }, + onLongClick = longClick, uiState = uiState, iconOnly = iconOnly, ) { @@ -192,18 +187,11 @@ fun Tile( tile.onSecondaryClick() } .takeIf { uiState.handlesSecondaryClick } - val longClick: (() -> Unit)? = - { - hapticsViewModel?.setTileInteractionState( - TileHapticsViewModel.TileInteractionState.LONG_CLICKED - ) - tile.onLongClick(expandable) - } - .takeIf { uiState.handlesLongClick } LargeTileContent( label = uiState.label, secondaryLabel = uiState.secondaryLabel, icon = icon, + sideDrawable = uiState.sideDrawable, colors = colors, iconShape = iconShape, toggleClick = secondaryClick, @@ -237,7 +225,7 @@ private fun TileExpandable( @Composable fun TileContainer( onClick: () -> Unit, - onLongClick: () -> Unit, + onLongClick: (() -> Unit)?, uiState: TileUiState, iconOnly: Boolean, content: @Composable BoxScope.() -> Unit, @@ -281,7 +269,7 @@ fun Modifier.tilePadding(): Modifier { @Composable fun Modifier.tileCombinedClickable( onClick: () -> Unit, - onLongClick: () -> Unit, + onLongClick: (() -> Unit)?, uiState: TileUiState, iconOnly: Boolean, ): Modifier { 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 9552aa935bbf..41c3de55af70 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 @@ -22,7 +22,7 @@ import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.setValue import com.android.systemui.qs.panels.ui.compose.selection.ResizingDefaults.RESIZING_THRESHOLD -class ResizingState(private val widths: TileWidths, private val onResize: () -> Unit) { +class ResizingState(val widths: TileWidths, private val onResize: () -> Unit) { /** Total drag offset of this resize operation. */ private var totalOffset by mutableFloatStateOf(0f) diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/DynamicIconTilesViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/DynamicIconTilesViewModel.kt index 9feaab83cc1f..a9d673aa7400 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/DynamicIconTilesViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/DynamicIconTilesViewModel.kt @@ -17,9 +17,13 @@ package com.android.systemui.qs.panels.ui.viewmodel import com.android.systemui.lifecycle.ExclusiveActivatable +import com.android.systemui.lifecycle.Hydrator import com.android.systemui.qs.panels.domain.interactor.DynamicIconTilesInteractor import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject +import kotlinx.coroutines.awaitCancellation +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.launch /** View model to resize QS tiles down to icons when removed from the current tiles. */ class DynamicIconTilesViewModel @@ -28,10 +32,21 @@ constructor( interactorFactory: DynamicIconTilesInteractor.Factory, iconTilesViewModel: IconTilesViewModel, ) : IconTilesViewModel by iconTilesViewModel, ExclusiveActivatable() { + private val hydrator = Hydrator("DynamicIconTilesViewModel") private val interactor = interactorFactory.create() + val largeTilesSpanState = + hydrator.hydratedStateOf( + traceName = "largeTilesSpan", + source = iconTilesViewModel.largeTilesSpan, + ) + override suspend fun onActivated(): Nothing { - interactor.activate() + coroutineScope { + launch { hydrator.activate() } + launch { interactor.activate() } + awaitCancellation() + } } @AssistedFactory diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/IconTilesViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/IconTilesViewModel.kt index 4e698edf4e34..b8c5fbb72614 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/IconTilesViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/IconTilesViewModel.kt @@ -25,6 +25,8 @@ import kotlinx.coroutines.flow.StateFlow interface IconTilesViewModel { val largeTiles: StateFlow<Set<TileSpec>> + val largeTilesSpan: StateFlow<Int> + fun isIconTile(spec: TileSpec): Boolean fun resize(spec: TileSpec, toIcon: Boolean) @@ -34,6 +36,7 @@ interface IconTilesViewModel { class IconTilesViewModelImpl @Inject constructor(private val interactor: IconTilesInteractor) : IconTilesViewModel { override val largeTiles = interactor.largeTilesSpecs + override val largeTilesSpan = interactor.largeTilesSpan override fun isIconTile(spec: TileSpec): Boolean = interactor.isIconTile(spec) diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModel.kt index 33ce5519b68c..adc4e4bf0870 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/QuickQuickSettingsViewModel.kt @@ -70,18 +70,21 @@ constructor( source = quickQuickSettingsRowInteractor.rows, ) + private val largeTilesSpan by + hydrator.hydratedStateOf( + traceName = "largeTilesSpan", + source = iconTilesViewModel.largeTilesSpan, + ) + private val currentTiles by hydrator.hydratedStateOf(traceName = "currentTiles", source = tilesInteractor.currentTiles) val tileViewModels by derivedStateOf { currentTiles - .map { SizedTileImpl(TileViewModel(it.tile, it.spec), it.spec.width) } + .map { SizedTileImpl(TileViewModel(it.tile, it.spec), it.spec.width()) } .let { splitInRowsSequence(it, columns).take(rows).toList().flatten() } } - private val TileSpec.width: Int - get() = if (largeTiles.contains(this)) 2 else 1 - override suspend fun onActivated(): Nothing { coroutineScope { launch { hydrator.activate() } @@ -95,4 +98,6 @@ constructor( interface Factory { fun create(): QuickQuickSettingsViewModel } + + private fun TileSpec.width(): Int = if (largeTiles.contains(this)) largeTilesSpan else 1 } diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileUiState.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileUiState.kt index 56675e49d4e6..2fc7f0f9d67b 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileUiState.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileUiState.kt @@ -17,6 +17,7 @@ package com.android.systemui.qs.panels.ui.viewmodel import android.content.res.Resources +import android.graphics.drawable.Drawable import android.service.quicksettings.Tile import android.text.TextUtils import android.widget.Switch @@ -36,6 +37,7 @@ data class TileUiState( val handlesLongClick: Boolean, val handlesSecondaryClick: Boolean, val icon: Supplier<QSTile.Icon?>, + val sideDrawable: Drawable?, val accessibilityUiState: AccessibilityUiState, ) @@ -90,6 +92,7 @@ fun QSTile.State.toUiState(resources: Resources): TileUiState { handlesLongClick = handlesLongClick, handlesSecondaryClick = handlesSecondaryClick, icon = icon?.let { Supplier { icon } } ?: iconSupplier ?: Supplier { null }, + sideDrawable = sideViewCustomDrawable, AccessibilityUiState( contentDescription?.toString() ?: "", stateDescription.toString(), diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java index f218d86a5aa1..37d24debe958 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java @@ -243,15 +243,15 @@ public class QuickAccessWalletTile extends QSTileImpl<QSTile.State> { } mSelectedCard = cards.get(selectedIndex); switch (mSelectedCard.getCardImage().getType()) { + case TYPE_BITMAP: + case TYPE_ADAPTIVE_BITMAP: + mCardViewDrawable = mSelectedCard.getCardImage().loadDrawable(mContext); + break; case TYPE_URI: case TYPE_URI_ADAPTIVE_BITMAP: - mCardViewDrawable = null; - break; case TYPE_RESOURCE: - case TYPE_BITMAP: - case TYPE_ADAPTIVE_BITMAP: case TYPE_DATA: - mCardViewDrawable = mSelectedCard.getCardImage().loadDrawable(mContext); + mCardViewDrawable = null; break; default: Log.e(TAG, "Unknown icon type: " + mSelectedCard.getCardImage().getType()); diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/UserAwareConnection.kt b/packages/SystemUI/src/com/android/systemui/recordissue/UserAwareConnection.kt index 6aaa27dd7387..eca405180075 100644 --- a/packages/SystemUI/src/com/android/systemui/recordissue/UserAwareConnection.kt +++ b/packages/SystemUI/src/com/android/systemui/recordissue/UserAwareConnection.kt @@ -69,7 +69,11 @@ open class UserAwareConnection( @WorkerThread fun doUnBind() { if (shouldUnBind) { - userContextProvider.userContext.unbindService(this) + try { + userContextProvider.userContext.unbindService(this) + } catch (e: IllegalArgumentException) { + Log.e(TAG, "Can't disconnect because service wasn't connected anyways.", e) + } shouldUnBind = false } } diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt index 580a51a3dc0a..daeaaa52fd94 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt @@ -373,6 +373,7 @@ constructor( "device was unlocked with alternate bouncer showing" + " and shade didn't need to be left open" } else { + replaceLockscreenSceneOnBackStack() null } } @@ -391,16 +392,7 @@ constructor( val prevScene = previousScene.value val targetScene = prevScene ?: Scenes.Gone if (targetScene != Scenes.Gone) { - sceneBackInteractor.updateBackStack { stack -> - val list = stack.asIterable().toMutableList() - check(list.last() == Scenes.Lockscreen) { - "The bottommost/last SceneKey of the back stack isn't" + - " the Lockscreen scene like expected. The back" + - " stack is $stack." - } - list[list.size - 1] = Scenes.Gone - sceneStackOf(*list.toTypedArray()) - } + replaceLockscreenSceneOnBackStack() } targetScene to "device was unlocked with primary bouncer showing," + @@ -435,6 +427,20 @@ constructor( } } + /** If the [Scenes.Lockscreen] is on the backstack, replaces it with [Scenes.Gone]. */ + private fun replaceLockscreenSceneOnBackStack() { + sceneBackInteractor.updateBackStack { stack -> + val list = stack.asIterable().toMutableList() + check(list.last() == Scenes.Lockscreen) { + "The bottommost/last SceneKey of the back stack isn't" + + " the Lockscreen scene like expected. The back" + + " stack is $stack." + } + list[list.size - 1] = Scenes.Gone + sceneStackOf(*list.toTypedArray()) + } + } + private fun handlePowerState() { applicationScope.launch { powerInteractor.detailedWakefulness.collect { wakefulness -> diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java index 24dba59a1d54..4d77e3ecea7b 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java @@ -17,8 +17,6 @@ package com.android.systemui.shade; import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; -import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; -import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_OPTIMIZE_MEASURE; import static com.android.systemui.statusbar.NotificationRemoteInputManager.ENABLE_REMOTE_INPUT; import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow; @@ -27,16 +25,13 @@ import android.app.IActivityManager; import android.content.Context; import android.content.pm.ActivityInfo; import android.content.res.Configuration; -import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.Region; -import android.os.Binder; import android.os.Build; import android.os.RemoteException; import android.os.Trace; import android.util.Log; import android.view.Display; -import android.view.Gravity; import android.view.IWindow; import android.view.IWindowSession; import android.view.View; @@ -271,33 +266,7 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW // Now that the notification shade encompasses the sliding panel and its // translucent backdrop, the entire thing is made TRANSLUCENT and is // hardware-accelerated. - mLp = new LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT, - LayoutParams.TYPE_NOTIFICATION_SHADE, - LayoutParams.FLAG_NOT_FOCUSABLE - | LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING - | LayoutParams.FLAG_SPLIT_TOUCH - | LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH - | LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, - PixelFormat.TRANSLUCENT); - mLp.token = new Binder(); - mLp.gravity = Gravity.TOP; - mLp.setFitInsetsTypes(0 /* types */); - mLp.setTitle("NotificationShade"); - mLp.packageName = mContext.getPackageName(); - mLp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; - mLp.privateFlags |= PRIVATE_FLAG_OPTIMIZE_MEASURE; - - if (SceneContainerFlag.isEnabled()) { - // This prevents the appearance and disappearance of the software keyboard (also known - // as the "IME") from scrolling/panning the window to make room for the keyboard. - // - // The scene container logic does its own adjustment and animation when the IME appears - // or disappears. - mLp.softInputMode = LayoutParams.SOFT_INPUT_ADJUST_NOTHING; - } - + mLp = ShadeWindowLayoutParams.INSTANCE.create(mContext); mWindowManager.addView(mWindowRootView, mLp); // We use BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE here, however, there is special logic in diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt index 42d4effbac3a..e15830eb22eb 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt @@ -20,6 +20,7 @@ import android.content.Context import android.content.res.Resources import android.view.LayoutInflater import android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY +import com.android.systemui.CoreStartable import com.android.systemui.common.ui.ConfigurationState import com.android.systemui.common.ui.ConfigurationStateImpl import com.android.systemui.common.ui.GlobalConfig @@ -29,12 +30,16 @@ 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.shade.data.repository.ShadePositionRepository +import com.android.systemui.shade.data.repository.ShadePositionRepositoryImpl import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround import com.android.systemui.statusbar.phone.ConfigurationControllerImpl import com.android.systemui.statusbar.phone.ConfigurationForwarder import com.android.systemui.statusbar.policy.ConfigurationController import dagger.Module import dagger.Provides +import dagger.multibindings.ClassKey +import dagger.multibindings.IntoMap /** * Module responsible for managing display-specific components and resources for the notification @@ -149,4 +154,24 @@ object ShadeDisplayAwareModule { configurationInteractor } } + + @SysUISingleton + @Provides + fun provideShadePositionRepository(impl: ShadePositionRepositoryImpl): ShadePositionRepository { + ShadeWindowGoesAround.isUnexpectedlyInLegacyMode() + return impl + } + + @Provides + @IntoMap + @ClassKey(ShadePositionRepositoryImpl::class) + fun provideShadePositionRepositoryAsCoreStartable( + impl: ShadePositionRepositoryImpl + ): CoreStartable { + return if (ShadeWindowGoesAround.isEnabled) { + impl + } else { + CoreStartable.NOP + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadePrimaryDisplayCommand.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadePrimaryDisplayCommand.kt new file mode 100644 index 000000000000..802fc0edb533 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadePrimaryDisplayCommand.kt @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.shade + +import android.view.Display +import com.android.systemui.shade.data.repository.ShadePositionRepository +import com.android.systemui.statusbar.commandline.Command +import java.io.PrintWriter + +class ShadePrimaryDisplayCommand(private val positionRepository: ShadePositionRepository) : + Command { + + override fun execute(pw: PrintWriter, args: List<String>) { + if (args[0].lowercase() == "reset") { + positionRepository.resetDisplayId() + pw.println("Reset shade primary display id to ${Display.DEFAULT_DISPLAY}") + return + } + + val displayId: Int = + try { + args[0].toInt() + } catch (e: NumberFormatException) { + pw.println("Error: task id should be an integer") + return + } + + if (displayId < 0) { + pw.println("Error: display id should be positive integer") + } + + positionRepository.setDisplayId(displayId) + pw.println("New shade primary display id is $displayId") + } + + 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 reset ") + pw.println("Reset the display which is holding the shade.") + } +} diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeWindowLayoutParams.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeWindowLayoutParams.kt new file mode 100644 index 000000000000..6bb50f99b5e7 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeWindowLayoutParams.kt @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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 + +import android.content.Context +import android.graphics.PixelFormat +import android.os.Binder +import android.view.Gravity +import android.view.ViewGroup +import android.view.WindowManager.LayoutParams +import com.android.systemui.scene.shared.flag.SceneContainerFlag + +object ShadeWindowLayoutParams { + /** + * Creates [LayoutParams] for the shade window. + * + * This is extracted to a single place as those layout params will be used by several places: + * - When sysui starts, and the shade is added the first time + * - When the shade moves to a different window (e.g. while an external display is connected) + */ + fun create(context: Context): LayoutParams { + return LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT, + LayoutParams.TYPE_NOTIFICATION_SHADE, + LayoutParams.FLAG_NOT_FOCUSABLE or + LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING or + LayoutParams.FLAG_SPLIT_TOUCH or + LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH or + LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, + // Now that the notification shade encompasses the sliding panel and its + // translucent backdrop, the entire thing is made TRANSLUCENT and is + // hardware-accelerated. + PixelFormat.TRANSLUCENT, + ) + .apply { + token = Binder() + gravity = Gravity.TOP + fitInsetsTypes = 0 + title = "NotificationShade" + packageName = context.packageName + layoutInDisplayCutoutMode = LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS + privateFlags = privateFlags or LayoutParams.PRIVATE_FLAG_OPTIMIZE_MEASURE + if (SceneContainerFlag.isEnabled) { + // This prevents the appearance and disappearance of the software keyboard (also + // known as the "IME") from scrolling/panning the window to make room for the + // keyboard. + // + // The scene container logic does its own adjustment and animation when the IME + // appears or disappears. + softInputMode = LayoutParams.SOFT_INPUT_ADJUST_NOTHING + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/shade/data/repository/FakeShadePositionRepository.kt b/packages/SystemUI/src/com/android/systemui/shade/data/repository/FakeShadePositionRepository.kt new file mode 100644 index 000000000000..37210b90ee78 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/shade/data/repository/FakeShadePositionRepository.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.shade.data.repository + +import android.view.Display +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow + +class FakeShadePositionRepository : ShadePositionRepository { + private val _displayId = MutableStateFlow(Display.DEFAULT_DISPLAY) + + override fun setDisplayId(displayId: Int) { + _displayId.value = displayId + } + + override val displayId: StateFlow<Int> + get() = _displayId + + override fun resetDisplayId() { + _displayId.value = Display.DEFAULT_DISPLAY + } +} diff --git a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadePositionRepository.kt b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadePositionRepository.kt new file mode 100644 index 000000000000..24c067ae2371 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadePositionRepository.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.shade.data.repository + +import android.view.Display +import com.android.systemui.CoreStartable +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.shade.ShadePrimaryDisplayCommand +import com.android.systemui.statusbar.commandline.CommandRegistry +import javax.inject.Inject +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow + +interface ShadePositionRepository { + /** 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() +} + +/** Source of truth for the display currently holding the shade. */ +@SysUISingleton +class ShadePositionRepositoryImpl +@Inject +constructor(private val commandRegistry: CommandRegistry) : ShadePositionRepository, CoreStartable { + 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 + } + + override fun start() { + commandRegistry.registerCommand("shade_display_override") { + ShadePrimaryDisplayCommand(this) + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/shade/shared/flag/ShadeWindowGoesAround.kt b/packages/SystemUI/src/com/android/systemui/shade/shared/flag/ShadeWindowGoesAround.kt index 6f492cfaa6c3..c23ff5302b3c 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/shared/flag/ShadeWindowGoesAround.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/shared/flag/ShadeWindowGoesAround.kt @@ -32,7 +32,7 @@ object ShadeWindowGoesAround { /** Is the refactor enabled */ @JvmStatic - inline val isEnabled + inline val isEnabled: Boolean get() = Flags.shadeWindowGoesAround() /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImpl.java index 3a31851bcb4f..52a79d39bb3b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImpl.java @@ -16,6 +16,9 @@ package com.android.systemui.statusbar.connectivity; +import static com.android.systemui.Flags.multiuserWifiPickerTrackerSupport; + +import android.content.Context; import android.content.Intent; import android.os.UserHandle; import android.os.UserManager; @@ -65,13 +68,16 @@ public class AccessPointControllerImpl implements AccessPointController, private final LifecycleRegistry mLifecycle = new LifecycleRegistry(this); private int mCurrentUser; + private Context mContext; public AccessPointControllerImpl( + Context context, UserManager userManager, UserTracker userTracker, Executor mainExecutor, WifiPickerTrackerFactory wifiPickerTrackerFactory ) { + mContext = context; mUserManager = userManager; mUserTracker = userTracker; mCurrentUser = userTracker.getUserId(); @@ -87,7 +93,11 @@ public class AccessPointControllerImpl implements AccessPointController, */ public void init() { if (mWifiPickerTracker == null) { - mWifiPickerTracker = mWifiPickerTrackerFactory.create(this.getLifecycle(), this, TAG); + // We are creating the WifiPickerTracker during init to make sure we have one + // available at all times however we expect this to be recreated very quickly + // with a user-specific context in onUserSwitched. + mWifiPickerTracker = + mWifiPickerTrackerFactory.create(mContext, this.getLifecycle(), this, TAG); } } @@ -116,6 +126,19 @@ public class AccessPointControllerImpl implements AccessPointController, void onUserSwitched(int newUserId) { mCurrentUser = newUserId; + // Return early if multiuser support is not enabled. + if (!multiuserWifiPickerTrackerSupport()) { + return; + } + + if (mWifiPickerTracker != null) { + mMainExecutor.execute(() -> mLifecycle.setCurrentState(Lifecycle.State.CREATED)); + } + Context context = mContext.createContextAsUser(UserHandle.of(newUserId), /* flags= */ 0); + mWifiPickerTracker = mWifiPickerTrackerFactory.create(context, mLifecycle, this, TAG); + if (!mCallbacks.isEmpty()) { + mMainExecutor.execute(() -> mLifecycle.setCurrentState(Lifecycle.State.STARTED)); + } } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiPickerTrackerFactory.kt b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiPickerTrackerFactory.kt index dc2ebe55fb73..947ce33274d3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiPickerTrackerFactory.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiPickerTrackerFactory.kt @@ -22,6 +22,7 @@ import android.net.wifi.WifiManager import android.os.Handler import android.os.SimpleClock import androidx.lifecycle.Lifecycle +import com.android.systemui.Flags.multiuserWifiPickerTrackerSupport import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.util.concurrency.ThreadFactory @@ -41,7 +42,7 @@ import javax.inject.Inject class WifiPickerTrackerFactory @Inject constructor( - private val context: Context, + private val applicationContext: Context, private val wifiManager: WifiManager?, private val connectivityManager: ConnectivityManager, private val systemClock: SystemClock, @@ -64,16 +65,23 @@ constructor( * @return a new [WifiPickerTracker] or null if [WifiManager] is null. */ fun create( + userContext: Context, lifecycle: Lifecycle, listener: WifiPickerTrackerCallback, name: String, ): WifiPickerTracker? { return if (wifiManager == null) { null - } else + } else { + val contextToUse = + if (multiuserWifiPickerTrackerSupport()) { + userContext + } else { + applicationContext + } WifiPickerTracker( lifecycle, - context, + contextToUse, wifiManager, connectivityManager, mainHandler, @@ -86,6 +94,7 @@ constructor( SCAN_INTERVAL_MILLIS, listener, ) + } } companion object { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt index f441fd644c17..4c54fc49e536 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt @@ -112,12 +112,12 @@ constructor( } override fun initializeStatusBar() { - StatusBarSimpleFragment.assertInLegacyMode() + StatusBarRootModernization.assertInLegacyMode() doStart() } private fun doStart() { - if (StatusBarSimpleFragment.isEnabled) doComposeStart() else doLegacyStart() + if (StatusBarRootModernization.isEnabled) doComposeStart() else doLegacyStart() } /** 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 f33b76b17f96..ff4760fd2837 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarOrchestrator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarOrchestrator.kt @@ -18,8 +18,10 @@ package com.android.systemui.statusbar.core import android.view.Display import android.view.View +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.CoreStartable import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor +import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.demomode.DemoModeController import com.android.systemui.dump.DumpManager import com.android.systemui.plugins.DarkIconDispatcher @@ -46,12 +48,12 @@ import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import java.io.PrintWriter import java.util.Optional +import kotlin.coroutines.CoroutineContext import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChangedBy import kotlinx.coroutines.flow.filterNotNull -import com.android.app.tracing.coroutines.launchTraced as launch /** * Class responsible for managing the lifecycle and state of the status bar. @@ -68,6 +70,7 @@ constructor( @Assisted private val statusBarModeRepository: StatusBarModePerDisplayRepository, @Assisted private val statusBarInitializer: StatusBarInitializer, @Assisted private val statusBarWindowController: StatusBarWindowController, + @Main private val mainContext: CoroutineContext, private val demoModeController: DemoModeController, private val pluginDependencyProvider: PluginDependencyProvider, private val autoHideController: AutoHideController, @@ -141,7 +144,8 @@ constructor( override fun start() { StatusBarConnectedDisplays.assertInNewMode() coroutineScope - .launch { + // Perform animations on the main thread to prevent crashes. + .launch(context = mainContext) { dumpManager.registerCriticalDumpable(dumpableName, this@StatusBarOrchestrator) launch { controllerAndBouncerShowing.collect { (controller, bouncerShowing) -> diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarSimpleFragment.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarRootModernization.kt index 214151383dc6..057213fa4b18 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarSimpleFragment.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarRootModernization.kt @@ -21,9 +21,9 @@ import com.android.systemui.flags.FlagToken import com.android.systemui.flags.RefactorFlagUtils /** Helper for reading and using the status bar simple fragment flag state */ -object StatusBarSimpleFragment { +object StatusBarRootModernization { /** Aconfig flag for removing the fragment */ - const val FLAG_NAME = Flags.FLAG_STATUS_BAR_SIMPLE_FRAGMENT + const val FLAG_NAME = Flags.FLAG_STATUS_BAR_ROOT_MODERNIZATION /** A token used for dependency declaration */ val token: FlagToken @@ -32,7 +32,7 @@ object StatusBarSimpleFragment { /** Is the refactor enabled */ @JvmStatic inline val isEnabled - get() = Flags.statusBarSimpleFragment() + get() = Flags.statusBarRootModernization() /** * Called to ensure code is only run when the flag is enabled. This protects users from the diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt index 564d52a62cde..1cb4c44d10e9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt @@ -27,7 +27,10 @@ import kotlinx.coroutines.flow.StateFlow interface SystemStatusAnimationScheduler : CallbackController<SystemStatusAnimationCallback>, Dumpable { - /** StateFlow holding the current [SystemEventAnimationState] at any time. */ + /** + * The current state of the animation. This can be used from compose functions to coordinate + * their animations with the chip + */ val animationState: StateFlow<SystemEventAnimationState> fun onStatusEvent(event: StatusEvent) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/data/repository/SystemStatusEventAnimationRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/data/repository/SystemStatusEventAnimationRepository.kt new file mode 100644 index 000000000000..971f5d1b04f2 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/data/repository/SystemStatusEventAnimationRepository.kt @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.events.data.repository + +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler +import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState +import javax.inject.Inject +import kotlinx.coroutines.flow.StateFlow + +/** Repository to expose the [SystemStatusAnimationScheduler] state via flows */ +interface SystemStatusEventAnimationRepository { + val animationState: StateFlow<SystemEventAnimationState> +} + +@SysUISingleton +class SystemStatusEventAnimationRepositoryImpl +@Inject +constructor(scheduler: SystemStatusAnimationScheduler) : SystemStatusEventAnimationRepository { + override val animationState = scheduler.animationState +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/domain/interactor/SystemStatusEventAnimationInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/domain/interactor/SystemStatusEventAnimationInteractor.kt new file mode 100644 index 000000000000..3e3064223595 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/domain/interactor/SystemStatusEventAnimationInteractor.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.statusbar.events.domain.interactor + +import android.view.View +import androidx.core.animation.Animator +import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.res.R +import com.android.systemui.statusbar.events.data.repository.SystemStatusEventAnimationRepository +import com.android.systemui.statusbar.phone.fragment.StatusBarSystemEventDefaultAnimator +import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.stateIn + +/** + * Interactor for dealing with system status event animations. This class can be used to monitor the + * current [animationState], and defines some common animation functions that an handle hiding + * system chrome in order to make space for the event chips + */ +@SysUISingleton +class SystemStatusEventAnimationInteractor +@Inject +constructor( + repo: SystemStatusEventAnimationRepository, + configurationInteractor: ConfigurationInteractor, + @Application scope: CoroutineScope, +) { + private val chipAnimateInTranslationX = + configurationInteractor + .dimensionPixelSize(R.dimen.ongoing_appops_chip_animation_in_status_bar_translation_x) + .stateIn(scope, SharingStarted.Eagerly, 0) + + private val chipAnimateOutTranslationX = + configurationInteractor + .dimensionPixelSize(R.dimen.ongoing_appops_chip_animation_out_status_bar_translation_x) + .stateIn(scope, SharingStarted.Eagerly, 0) + + val animationState = repo.animationState + + private fun getDefaultStatusBarAnimationForChipEnter( + setX: (Float) -> Unit, + setAlpha: (Float) -> Unit, + ): Animator { + return StatusBarSystemEventDefaultAnimator.getDefaultStatusBarAnimationForChipEnter( + chipAnimateInTranslationX.value, + setX, + setAlpha, + ) + } + + private fun getDefaultStatusBarAnimationForChipExit( + setX: (Float) -> Unit, + setAlpha: (Float) -> Unit, + ): Animator { + return StatusBarSystemEventDefaultAnimator.getDefaultStatusBarAnimationForChipExit( + chipAnimateOutTranslationX.value, + setX, + setAlpha, + ) + } + + fun animateStatusBarContentForChipEnter(v: View) { + getDefaultStatusBarAnimationForChipEnter(setX = v::setTranslationX, setAlpha = v::setAlpha) + .start() + } + + fun animateStatusBarContentForChipExit(v: View) { + v.translationX = chipAnimateOutTranslationX.value.toFloat() + getDefaultStatusBarAnimationForChipExit(setX = v::setTranslationX, setAlpha = v::setAlpha) + .start() + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollectionCache.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollectionCache.kt index 958001625a07..1f8d365cfdad 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollectionCache.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollectionCache.kt @@ -102,6 +102,10 @@ class NotifCollectionCache<V>( return --lives <= 0 } } + + override fun toString(): String { + return "$key = $value" + } } /** @@ -174,7 +178,10 @@ class NotifCollectionCache<V>( pw.println("$TAG(retainCount = $retainCount, purgeTimeoutMillis = $purgeTimeoutMillis)") pw.withIncreasedIndent { - pw.printCollection("keys present in cache", cache.keys.stream().sorted().toList()) + pw.printCollection( + "entries present in cache", + cache.values.stream().map { it.toString() }.sorted().toList(), + ) val misses = misses.get() val hits = hits.get() diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java index e74ed8d27ec2..c487ff5d35bd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java @@ -49,6 +49,7 @@ import android.os.SystemClock; import android.service.notification.NotificationListenerService.Ranking; import android.service.notification.SnoozeCriterion; import android.service.notification.StatusBarNotification; +import android.util.Log; import android.view.ContentInfo; import androidx.annotation.NonNull; @@ -65,6 +66,7 @@ import com.android.systemui.statusbar.notification.collection.notifcollection.No import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender; import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager; 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; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowController; import com.android.systemui.statusbar.notification.row.NotificationGuts; @@ -194,6 +196,10 @@ public final class NotificationEntry extends ListEntry { */ private boolean mIsDemoted = false; + // TODO(b/377565433): Move into NotificationContentModel during/after + // NotificationRowContentBinderRefactor. + private PromotedNotificationContentModel mPromotedNotificationContentModel; + /** * True if both * 1) app provided full screen intent but does not have the permission to send it @@ -1061,6 +1067,32 @@ public final class NotificationEntry extends ListEntry { this.mHeadsUpStatusBarTextPublic.setValue(headsUpStatusBarModel.getPublicText()); } + /** + * Gets the content needed to render this notification as a promoted notification on various + * surfaces (like status bar chips and AOD). + */ + public PromotedNotificationContentModel getPromotedNotificationContentModel() { + if (PromotedNotificationContentModel.featureFlagEnabled()) { + return mPromotedNotificationContentModel; + } else { + Log.wtf(TAG, "getting promoted content without feature flag enabled"); + return null; + } + } + + /** + * Sets the content needed to render this notification as a promoted notification on various + * surfaces (like status bar chips and AOD). + */ + public void setPromotedNotificationContentModel( + @Nullable PromotedNotificationContentModel promotedNotificationContentModel) { + if (PromotedNotificationContentModel.featureFlagEnabled()) { + this.mPromotedNotificationContentModel = promotedNotificationContentModel; + } else { + Log.wtf(TAG, "setting promoted content without feature flag enabled"); + } + } + /** Information about a suggestion that is being edited. */ public static class EditedSuggestionInfo { @@ -1101,4 +1133,6 @@ public final class NotificationEntry extends ListEntry { private static final long INITIALIZATION_DELAY = 400; private static final long NOT_LAUNCHED_YET = -LAUNCH_COOLDOWN; private static final int COLOR_INVALID = 1; + + private static final String TAG = "NotificationEntry"; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/LockScreenMinimalismCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/LockScreenMinimalismCoordinator.kt index 2fded34a56a0..e2328497d9ea 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/LockScreenMinimalismCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/LockScreenMinimalismCoordinator.kt @@ -19,6 +19,7 @@ package com.android.systemui.statusbar.notification.collection.coordinator import android.annotation.SuppressLint import android.app.NotificationManager 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 @@ -50,7 +51,6 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map -import com.android.app.tracing.coroutines.launchTraced as launch /** * If the setting is enabled, this will track seen notifications and ensure that they only show in @@ -74,7 +74,7 @@ constructor( private val unseenNotifications = mutableSetOf<NotificationEntry>() private var isShadeVisible = false - private var unseenFilterEnabled = false + private var minimalismEnabled = false override fun attach(pipeline: NotifPipeline) { if (NotificationMinimalism.isUnexpectedlyInLegacyMode()) { @@ -83,7 +83,7 @@ constructor( pipeline.addPromoter(unseenNotifPromoter) pipeline.addOnBeforeTransformGroupsListener(::pickOutTopUnseenNotifs) pipeline.addCollectionListener(collectionListener) - scope.launch { trackUnseenFilterSettingChanges() } + scope.launch { trackLockScreenNotificationMinimalismSettingChanges() } dumpManager.registerDumpable(this) } @@ -136,12 +136,12 @@ constructor( return seenNotificationsInteractor.isLockScreenNotificationMinimalismEnabled() } - private suspend fun trackUnseenFilterSettingChanges() { + private suspend fun trackLockScreenNotificationMinimalismSettingChanges() { // Only filter the seen notifs when the lock screen minimalism feature settings is on. minimalismFeatureSettingEnabled().collectLatest { isMinimalismSettingEnabled -> // update local field and invalidate if necessary - if (isMinimalismSettingEnabled != unseenFilterEnabled) { - unseenFilterEnabled = isMinimalismSettingEnabled + if (isMinimalismSettingEnabled != minimalismEnabled) { + minimalismEnabled = isMinimalismSettingEnabled unseenNotifications.clear() unseenNotifPromoter.invalidateList("unseen setting changed") } @@ -156,21 +156,21 @@ constructor( private val collectionListener = object : NotifCollectionListener { override fun onEntryAdded(entry: NotificationEntry) { - if (unseenFilterEnabled && !isShadeVisible) { + if (minimalismEnabled && !isShadeVisible) { logger.logUnseenAdded(entry.key) unseenNotifications.add(entry) } } override fun onEntryUpdated(entry: NotificationEntry) { - if (unseenFilterEnabled && !isShadeVisible) { + if (minimalismEnabled && !isShadeVisible) { logger.logUnseenUpdated(entry.key) unseenNotifications.add(entry) } } override fun onEntryRemoved(entry: NotificationEntry, reason: Int) { - if (unseenFilterEnabled && unseenNotifications.remove(entry)) { + if (minimalismEnabled && unseenNotifications.remove(entry)) { logger.logUnseenRemoved(entry.key) } } @@ -178,7 +178,7 @@ constructor( private fun pickOutTopUnseenNotifs(list: List<ListEntry>) { if (NotificationMinimalism.isUnexpectedlyInLegacyMode()) return - if (!unseenFilterEnabled) return + if (!minimalismEnabled) return // Only ever elevate a top unseen notification on keyguard, not even locked shade if (statusBarStateController.state != StatusBarState.KEYGUARD) { seenNotificationsInteractor.setTopOngoingNotification(null) @@ -215,6 +215,7 @@ constructor( override fun shouldPromoteToTopLevel(child: NotificationEntry): Boolean = when { NotificationMinimalism.isUnexpectedlyInLegacyMode() -> false + !minimalismEnabled -> false seenNotificationsInteractor.isTopOngoingNotification(child) -> true !NotificationMinimalism.ungroupTopUnseen -> false else -> seenNotificationsInteractor.isTopUnseenNotification(child) @@ -225,6 +226,7 @@ constructor( object : NotifSectioner("TopOngoing", BUCKET_TOP_ONGOING) { override fun isInSection(entry: ListEntry): Boolean { if (NotificationMinimalism.isUnexpectedlyInLegacyMode()) return false + if (!minimalismEnabled) return false return entry.anyEntry { notificationEntry -> seenNotificationsInteractor.isTopOngoingNotification(notificationEntry) } @@ -235,6 +237,7 @@ constructor( object : NotifSectioner("TopUnseen", BUCKET_TOP_UNSEEN) { override fun isInSection(entry: ListEntry): Boolean { if (NotificationMinimalism.isUnexpectedlyInLegacyMode()) return false + if (!minimalismEnabled) return false return entry.anyEntry { notificationEntry -> seenNotificationsInteractor.isTopUnseenNotification(notificationEntry) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt index 23da90d426c7..8edbc5e8e4bb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt @@ -34,6 +34,7 @@ import com.android.systemui.statusbar.notification.collection.provider.SectionSt import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore import com.android.systemui.statusbar.notification.promoted.PromotedNotificationsProvider +import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel import com.android.systemui.statusbar.notification.shared.ActiveNotificationEntryModel import com.android.systemui.statusbar.notification.shared.ActiveNotificationGroupModel import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel @@ -173,6 +174,7 @@ private class ActiveNotificationsStoreBuilder( isGroupSummary = sbn.notification.isGroupSummary, bucket = bucket, callType = sbn.toCallType(), + promotedContent = promotedNotificationContentModel, ) } } @@ -199,6 +201,7 @@ private fun ActiveNotificationsStore.createOrReuse( isGroupSummary: Boolean, bucket: Int, callType: CallType, + promotedContent: PromotedNotificationContentModel?, ): ActiveNotificationModel { return individuals[key]?.takeIf { it.isCurrent( @@ -223,6 +226,7 @@ private fun ActiveNotificationsStore.createOrReuse( contentIntent = contentIntent, bucket = bucket, callType = callType, + promotedContent = promotedContent, ) } ?: ActiveNotificationModel( @@ -247,6 +251,7 @@ private fun ActiveNotificationsStore.createOrReuse( contentIntent = contentIntent, bucket = bucket, callType = callType, + promotedContent = promotedContent, ) } @@ -272,6 +277,7 @@ private fun ActiveNotificationModel.isCurrent( isGroupSummary: Boolean, bucket: Int, callType: CallType, + promotedContent: PromotedNotificationContentModel?, ): Boolean { return when { key != this.key -> false @@ -295,6 +301,9 @@ private fun ActiveNotificationModel.isCurrent( contentIntent != this.contentIntent -> false bucket != this.bucket -> false callType != this.callType -> false + // QQQ: Do we need to do the same `isCurrent` thing within the content model to avoid + // recreating the active notification model constantly? + promotedContent != this.promotedContent -> false else -> true } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt new file mode 100644 index 000000000000..41ee3b992c5a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.promoted.shared.model + +import android.annotation.DrawableRes +import android.graphics.drawable.Icon +import com.android.internal.widget.NotificationProgressModel +import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips +import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUi + +/** + * The content needed to render a promoted notification to surfaces besides the notification stack, + * like the skeleton view on AOD or the status bar chip. + */ +data class PromotedNotificationContentModel( + val key: String, + + // for all styles: + val skeletonSmallIcon: Icon?, // TODO(b/377568176): Make into an IconModel. + val appName: CharSequence?, + val subText: CharSequence?, + val time: When?, + val lastAudiblyAlertedMs: Long, + @DrawableRes val profileBadgeResId: Int?, + val title: CharSequence?, + val text: CharSequence?, + val skeletonLargeIcon: Icon?, // TODO(b/377568176): Make into an IconModel. + val style: Style, + + // for CallStyle: + val personIcon: Icon?, // TODO(b/377568176): Make into an IconModel. + val personName: CharSequence?, + val verificationIcon: Icon?, // TODO(b/377568176): Make into an IconModel. + val verificationText: CharSequence?, + + // for ProgressStyle: + val progress: NotificationProgressModel?, +) { + class Builder(val key: String) { + var skeletonSmallIcon: Icon? = null + var appName: CharSequence? = null + var subText: CharSequence? = null + var time: When? = null + var lastAudiblyAlertedMs: Long = 0L + @DrawableRes var profileBadgeResId: Int? = null + var title: CharSequence? = null + var text: CharSequence? = null + var skeletonLargeIcon: Icon? = null + var style: Style = Style.Ineligible + + // for CallStyle: + var personIcon: Icon? = null + var personName: CharSequence? = null + var verificationIcon: Icon? = null + var verificationText: CharSequence? = null + + // for ProgressStyle: + var progress: NotificationProgressModel? = null + + fun build() = + PromotedNotificationContentModel( + key = key, + skeletonSmallIcon = skeletonSmallIcon, + appName = appName, + subText = subText, + time = time, + lastAudiblyAlertedMs = lastAudiblyAlertedMs, + profileBadgeResId = profileBadgeResId, + title = title, + text = text, + skeletonLargeIcon = skeletonLargeIcon, + style = style, + personIcon = personIcon, + personName = personName, + verificationIcon = verificationIcon, + verificationText = verificationText, + progress = progress, + ) + } + + /** The timestamp associated with a notification, along with the mode used to display it. */ + data class When(val time: Long, val mode: Mode) { + /** The mode used to display a notification's `when` value. */ + enum class Mode { + Absolute, + CountDown, + CountUp, + } + } + + /** The promotion-eligible style of a notification, or [Style.Ineligible] if not. */ + enum class Style { + BigPicture, + BigText, + Call, + Progress, + Ineligible, + } + + companion object { + @JvmStatic + fun featureFlagEnabled(): Boolean = + PromotedNotificationUi.isEnabled || StatusBarNotifChips.isEnabled + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/ActiveNotificationModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/ActiveNotificationModel.kt index 19a92a2230ba..a2b71551eca8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/ActiveNotificationModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/ActiveNotificationModel.kt @@ -17,7 +17,9 @@ package com.android.systemui.statusbar.notification.shared import android.app.PendingIntent import android.graphics.drawable.Icon +import android.util.Log import com.android.systemui.statusbar.StatusBarIconView +import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel import com.android.systemui.statusbar.notification.stack.PriorityBucket /** @@ -36,6 +38,7 @@ data class ActiveNotificationModel( val groupKey: String?, /** When this notification was posted. */ val whenTime: Long, + // TODO(b/377566661): Make isPromoted just check if promotedContent != null. /** True if this notification should be promoted and false otherwise. */ val isPromoted: Boolean, /** Is this entry in the ambient / minimized section (lowest priority)? */ @@ -78,7 +81,24 @@ data class ActiveNotificationModel( @PriorityBucket val bucket: Int, /** The call type set on the notification. */ val callType: CallType, -) : ActiveNotificationEntryModel() + /** + * The content needed to render this as a promoted notification on various surfaces, or null if + * this notification cannot be rendered as a promoted notification. + */ + val promotedContent: PromotedNotificationContentModel?, +) : ActiveNotificationEntryModel() { + init { + if (!PromotedNotificationContentModel.featureFlagEnabled()) { + if (promotedContent != null) { + Log.wtf(TAG, "passing non-null promoted content without feature flag enabled") + } + } + } + + companion object { + private const val TAG = "ActiveNotificationEntryModel" + } +} /** Model for a group of notifications. */ data class ActiveNotificationGroupModel( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt index 3bc549543ef2..5dff8120f33f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt @@ -21,12 +21,14 @@ import android.util.Log import android.view.View.GONE import androidx.annotation.VisibleForTesting import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.media.controls.domain.pipeline.MediaDataManager import com.android.systemui.res.R import com.android.systemui.statusbar.LockscreenShadeTransitionController import com.android.systemui.statusbar.StatusBarState.KEYGUARD import com.android.systemui.statusbar.SysuiStatusBarStateController +import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.ExpandableView import com.android.systemui.statusbar.notification.shared.NotificationMinimalism @@ -38,6 +40,9 @@ import javax.inject.Inject import kotlin.math.max import kotlin.math.min import kotlin.properties.Delegates.notNull +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.launch private const val TAG = "NotifStackSizeCalc" private val DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG) @@ -56,7 +61,9 @@ constructor( private val lockscreenShadeTransitionController: LockscreenShadeTransitionController, private val mediaDataManager: MediaDataManager, @Main private val resources: Resources, - private val splitShadeStateController: SplitShadeStateController + private val splitShadeStateController: SplitShadeStateController, + private val seenNotificationsInteractor: SeenNotificationsInteractor, + @Application private val scope: CoroutineScope, ) { /** @@ -74,7 +81,7 @@ constructor( /** Whether we allow keyguard to show less important notifications above the shelf. */ private val limitLockScreenToOneImportant - get() = NotificationMinimalism.isEnabled + get() = NotificationMinimalism.isEnabled && minimalismSettingEnabled /** Minimum space between two notifications, see [calculateGapAndDividerHeight]. */ private var dividerHeight by notNull<Float>() @@ -85,8 +92,14 @@ constructor( */ private var saveSpaceOnLockscreen = false + /** True when the lock screen notification minimalism feature setting is enabled */ + private var minimalismSettingEnabled = false + init { updateResources() + if (NotificationMinimalism.isEnabled) { + scope.launch { trackLockScreenNotificationMinimalismSettingChanges() } + } } private fun allowedByPolicy(stackHeight: StackHeight): Boolean = @@ -199,7 +212,7 @@ constructor( canStackFitInSpace( heightResult, notifSpace = notifSpace, - shelfSpace = shelfSpace + shelfSpace = shelfSpace, ) == FitResult.FIT } @@ -229,7 +242,7 @@ constructor( canStackFitInSpace( heightResult, notifSpace = notifSpace, - shelfSpace = shelfSpace + shelfSpace = shelfSpace, ) != FitResult.NO_FIT } log { "\t--- maxNotifications=$maxNotifications" } @@ -277,7 +290,7 @@ constructor( fun computeHeight( stack: NotificationStackScrollLayout, maxNotifs: Int, - shelfHeight: Float + shelfHeight: Float, ): Float { log { "\n" } log { "computeHeight ---" } @@ -311,7 +324,7 @@ constructor( private enum class FitResult { FIT, FIT_IF_SAVE_SPACE, - NO_FIT + NO_FIT, } data class SpaceNeeded( @@ -319,7 +332,7 @@ constructor( val whenEnoughSpace: Float, // Float height of space needed when showing collapsed layout for FSI HUNs. - val whenSavingSpace: Float + val whenSavingSpace: Float, ) private data class StackHeight( @@ -335,9 +348,19 @@ constructor( val shelfHeightWithSpaceBefore: Float, /** Whether the stack should actually be forced into the shelf before this height. */ - val shouldForceIntoShelf: Boolean + val shouldForceIntoShelf: Boolean, ) + private suspend fun trackLockScreenNotificationMinimalismSettingChanges() { + if (NotificationMinimalism.isUnexpectedlyInLegacyMode()) return + seenNotificationsInteractor.isLockScreenNotificationMinimalismEnabled().collectLatest { + if (it != minimalismSettingEnabled) { + minimalismSettingEnabled = it + } + Log.i(TAG, "minimalismSettingEnabled: $minimalismSettingEnabled") + } + } + private fun computeHeightPerNotificationLimit( stack: NotificationStackScrollLayout, shelfHeight: Float, @@ -377,7 +400,7 @@ constructor( stack, previous = currentNotification, current = children[firstViewInShelfIndex], - currentIndex = firstViewInShelfIndex + currentIndex = firstViewInShelfIndex, ) spaceBeforeShelf + shelfHeight } @@ -390,14 +413,15 @@ constructor( log { "\tcomputeHeightPerNotificationLimit i=$i notifs=$notifications " + "notifsHeightSavingSpace=$notifsWithCollapsedHun" + - " shelfWithSpaceBefore=$shelfWithSpaceBefore" + " shelfWithSpaceBefore=$shelfWithSpaceBefore" + + " limitLockScreenToOneImportant: $limitLockScreenToOneImportant" } yield( StackHeight( notifsHeight = notifications, notifsHeightSavingSpace = notifsWithCollapsedHun, shelfHeightWithSpaceBefore = shelfWithSpaceBefore, - shouldForceIntoShelf = counter?.shouldForceIntoShelf() ?: false + shouldForceIntoShelf = counter?.shouldForceIntoShelf() ?: false, ) ) } @@ -462,6 +486,10 @@ constructor( fun dump(pw: PrintWriter, args: Array<out String>) { pw.println("NotificationStackSizeCalculator saveSpaceOnLockscreen=$saveSpaceOnLockscreen") + pw.println( + "NotificationStackSizeCalculator " + + "limitLockScreenToOneImportant=$limitLockScreenToOneImportant" + ) } private fun ExpandableView.isShowable(onLockscreen: Boolean): Boolean { @@ -484,7 +512,7 @@ constructor( stack: NotificationStackScrollLayout, previous: ExpandableView?, current: ExpandableView?, - currentIndex: Int + currentIndex: Int, ): Float { if (currentIndex == 0) { return 0f @@ -536,11 +564,7 @@ constructor( takeWhile(predicate).count() - 1 /** Counts the number of notifications for each type of bucket */ - data class BucketTypeCounter( - var ongoing: Int = 0, - var important: Int = 0, - var other: Int = 0, - ) { + data class BucketTypeCounter(var ongoing: Int = 0, var important: Int = 0, var other: Int = 0) { fun incrementForBucket(@PriorityBucket bucket: Int?) { when (bucket) { BUCKET_MEDIA_CONTROLS, 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 7389086296a3..80c8e8b2a109 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java @@ -203,7 +203,7 @@ import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.core.StatusBarConnectedDisplays; import com.android.systemui.statusbar.core.StatusBarInitializer; -import com.android.systemui.statusbar.core.StatusBarSimpleFragment; +import com.android.systemui.statusbar.core.StatusBarRootModernization; import com.android.systemui.statusbar.data.model.StatusBarMode; import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore; import com.android.systemui.statusbar.notification.NotificationActivityStarter; @@ -1231,7 +1231,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { checkBarModes(); }); } - if (!StatusBarSimpleFragment.isEnabled() && !StatusBarConnectedDisplays.isEnabled()) { + if (!StatusBarRootModernization.isEnabled() && !StatusBarConnectedDisplays.isEnabled()) { // When the flag is on, we register the fragment as a core startable and this is not // needed mStatusBarInitializer.initializeStatusBar(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt index d991b1df45fe..41db5f450df8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt @@ -174,6 +174,14 @@ constructor( private val dumpableName = TAG + nameSuffix private val commandName = StatusBarInsetsCommand.NAME + nameSuffix + init { + if (!StatusBarConnectedDisplays.isEnabled) { + // Call start(), since it is not called when the flag is disabled, to keep the old + // behavior as it was. + start() + } + } + override fun start() { configurationController.addCallback(this) dumpManager.registerDumpable(dumpableName, this) 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 0c511aeae3e5..aef26dea3c0d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -520,6 +520,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mListenForCanShowAlternateBouncer.cancel(null); } mListenForCanShowAlternateBouncer = null; + // Collector that keeps the AlternateBouncerInteractor#canShowAlternateBouncer flow hot. mListenForCanShowAlternateBouncer = mJavaAdapter.alwaysCollectFlow( mAlternateBouncerInteractor.getCanShowAlternateBouncer(), @@ -568,6 +569,12 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb } private void consumeCanShowAlternateBouncer(boolean canShow) { + if (SceneContainerFlag.isEnabled()) { + // When the scene framework is enabled, the alternative bouncer is hidden from the scene + // framework logic so there's no need for this logic here. + return; + } + // Hack: this is required to fix issues where // KeyguardBouncerRepository#alternateBouncerVisible state is incorrectly set and then never // reset. This is caused by usages of show()/forceShow() that only read this flow to set the 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 ba878edc1132..58386b0cad7c 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 @@ -30,7 +30,7 @@ import com.android.systemui.statusbar.core.StatusBarInitializer import com.android.systemui.statusbar.core.StatusBarInitializerImpl import com.android.systemui.statusbar.core.StatusBarInitializerStore import com.android.systemui.statusbar.core.StatusBarOrchestrator -import com.android.systemui.statusbar.core.StatusBarSimpleFragment +import com.android.systemui.statusbar.core.StatusBarRootModernization import com.android.systemui.statusbar.data.repository.PrivacyDotViewControllerStoreModule import com.android.systemui.statusbar.data.repository.PrivacyDotWindowControllerStoreModule import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore @@ -87,7 +87,7 @@ interface StatusBarPhoneModule { return if (StatusBarConnectedDisplays.isEnabled) { // Will be started through MultiDisplayStatusBarStarter CoreStartable.NOP - } else if (StatusBarSimpleFragment.isEnabled) { + } else if (StatusBarRootModernization.isEnabled) { defaultInitializerLazy.get() } else { // Will be started through CentralSurfaces diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java index 5cc44762dde8..23b4b65bb2ac 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java @@ -58,7 +58,7 @@ import com.android.systemui.statusbar.OperatorNameViewController; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips; import com.android.systemui.statusbar.core.StatusBarConnectedDisplays; -import com.android.systemui.statusbar.core.StatusBarSimpleFragment; +import com.android.systemui.statusbar.core.StatusBarRootModernization; import com.android.systemui.statusbar.disableflags.DisableFlagsLogger; import com.android.systemui.statusbar.events.SystemStatusAnimationCallback; import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler; @@ -365,7 +365,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue mPrimaryOngoingActivityChip = mStatusBar.findViewById(R.id.ongoing_activity_chip_primary); mSecondaryOngoingActivityChip = mStatusBar.findViewById(R.id.ongoing_activity_chip_secondary); - if (!StatusBarSimpleFragment.isEnabled()) { + if (!StatusBarRootModernization.isEnabled()) { showEndSideContent(false); showClock(false); } @@ -376,7 +376,11 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue mCarrierConfigTracker.addDefaultDataSubscriptionChangedListener(mDefaultDataListener); mHomeStatusBarViewBinder.bind( - mStatusBar, mHomeStatusBarViewModel, mStatusBarVisibilityChangeListener); + mStatusBar, + mHomeStatusBarViewModel, + /* systemEventChipAnimateIn */ null, + /* systemEventChipAnimateOut */ null, + mStatusBarVisibilityChangeListener); } private String getDumpableName() { @@ -464,7 +468,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue super.onPause(); mCommandQueue.removeCallback(this); mStatusBarStateController.removeCallback(this); - if (!StatusBarSimpleFragment.isEnabled()) { + if (!StatusBarRootModernization.isEnabled()) { mOngoingCallController.removeCallback(mOngoingCallListener); } mAnimationScheduler.removeCallback(this); @@ -507,7 +511,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue mNicBindingDisposable = mNicViewBinder.bindWhileAttached(notificationIcons, displayId); } - if (!StatusBarSimpleFragment.isEnabled()) { + if (!StatusBarRootModernization.isEnabled()) { updateNotificationIconAreaAndOngoingActivityChip(/* animate= */ false); } Trace.endSection(); @@ -528,7 +532,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue new StatusBarVisibilityChangeListener() { @Override public void onStatusBarVisibilityMaybeChanged() { - if (StatusBarSimpleFragment.isEnabled()) { + if (StatusBarRootModernization.isEnabled()) { return; } updateStatusBarVisibilities(/* animate= */ true); @@ -536,7 +540,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue @Override public void onTransitionFromLockscreenToDreamStarted() { - if (StatusBarSimpleFragment.isEnabled()) { + if (StatusBarRootModernization.isEnabled()) { return; } mTransitionFromLockscreenToDreamStarted = true; @@ -547,7 +551,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue boolean hasPrimaryOngoingActivity, boolean hasSecondaryOngoingActivity, boolean shouldAnimate) { - if (StatusBarSimpleFragment.isEnabled()) { + if (StatusBarRootModernization.isEnabled()) { return; } mHasPrimaryOngoingActivity = hasPrimaryOngoingActivity; @@ -558,7 +562,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue @Override public void onIsHomeStatusBarAllowedBySceneChanged( boolean isHomeStatusBarAllowedByScene) { - if (StatusBarSimpleFragment.isEnabled()) { + if (StatusBarRootModernization.isEnabled()) { return; } mHomeStatusBarAllowedByScene = isHomeStatusBarAllowedByScene; @@ -568,7 +572,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue @Override public void disable(int displayId, int state1, int state2, boolean animate) { - if (StatusBarSimpleFragment.isEnabled()) { + if (StatusBarRootModernization.isEnabled()) { return; } if (displayId != getContext().getDisplayId()) { @@ -582,7 +586,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue } private void updateStatusBarVisibilities(boolean animate) { - StatusBarSimpleFragment.assertInLegacyMode(); + StatusBarRootModernization.assertInLegacyMode(); StatusBarVisibilityModel previousModel = mLastModifiedVisibility; StatusBarVisibilityModel newModel = calculateInternalModel(mLastSystemVisibility); @@ -623,7 +627,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue private StatusBarVisibilityModel calculateInternalModel( StatusBarVisibilityModel externalModel) { - StatusBarSimpleFragment.assertInLegacyMode(); + StatusBarRootModernization.assertInLegacyMode(); // TODO(b/328393714) use HeadsUpNotificationInteractor.showHeadsUpStatusBar instead. boolean headsUpVisible = @@ -677,7 +681,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue * mLastModifiedVisibility. */ private void updateNotificationIconAreaAndOngoingActivityChip(boolean animate) { - StatusBarSimpleFragment.assertInLegacyMode(); + StatusBarRootModernization.assertInLegacyMode(); StatusBarVisibilityModel visibilityModel = mLastModifiedVisibility; boolean disableNotifications = !visibilityModel.getShowNotificationIcons(); @@ -714,7 +718,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue } private boolean shouldHideStatusBar() { - StatusBarSimpleFragment.assertInLegacyMode(); + StatusBarRootModernization.assertInLegacyMode(); boolean isDefaultDisplay = getContext().getDisplayId() == Display.DEFAULT_DISPLAY; boolean shouldHideForCurrentDisplay = @@ -776,7 +780,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue } private void hideEndSideContent(boolean animate) { - StatusBarSimpleFragment.assertInLegacyMode(); + StatusBarRootModernization.assertInLegacyMode(); if (!animate || !mAnimationsEnabled) { mEndSideAlphaController.setAlpha(/*alpha*/ 0f, SOURCE_OTHER); } else { @@ -786,7 +790,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue } private void showEndSideContent(boolean animate) { - StatusBarSimpleFragment.assertInLegacyMode(); + StatusBarRootModernization.assertInLegacyMode(); if (!animate || !mAnimationsEnabled) { mEndSideAlphaController.setAlpha(1f, SOURCE_OTHER); return; @@ -803,18 +807,18 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue } private void hideClock(boolean animate) { - StatusBarSimpleFragment.assertInLegacyMode(); + StatusBarRootModernization.assertInLegacyMode(); animateHiddenState(mClockView, clockHiddenMode(), animate); } private void showClock(boolean animate) { - StatusBarSimpleFragment.assertInLegacyMode(); + StatusBarRootModernization.assertInLegacyMode(); animateShow(mClockView, animate); } /** Hides the primary ongoing activity chip. */ private void hidePrimaryOngoingActivityChip(boolean animate) { - StatusBarSimpleFragment.assertInLegacyMode(); + StatusBarRootModernization.assertInLegacyMode(); animateHiddenState(mPrimaryOngoingActivityChip, View.GONE, animate); } @@ -826,18 +830,18 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue * activities. See b/332662551. */ private void showPrimaryOngoingActivityChip(boolean animate) { - StatusBarSimpleFragment.assertInLegacyMode(); + StatusBarRootModernization.assertInLegacyMode(); animateShow(mPrimaryOngoingActivityChip, animate); } private void hideSecondaryOngoingActivityChip(boolean animate) { - StatusBarSimpleFragment.assertInLegacyMode(); + StatusBarRootModernization.assertInLegacyMode(); animateHiddenState(mSecondaryOngoingActivityChip, View.GONE, animate); } private void showSecondaryOngoingActivityChip(boolean animate) { StatusBarNotifChips.assertInNewMode(); - StatusBarSimpleFragment.assertInLegacyMode(); + StatusBarRootModernization.assertInLegacyMode(); animateShow(mSecondaryOngoingActivityChip, animate); } @@ -846,7 +850,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue * don't set the clock GONE otherwise it'll mess up the animation. */ private int clockHiddenMode() { - StatusBarSimpleFragment.assertInLegacyMode(); + StatusBarRootModernization.assertInLegacyMode(); if (!mShadeExpansionStateManager.isClosed() && !mKeyguardStateController.isShowing() && !mStatusBarStateController.isDozing()) { return View.INVISIBLE; @@ -855,24 +859,24 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue } public void hideNotificationIconArea(boolean animate) { - StatusBarSimpleFragment.assertInLegacyMode(); + StatusBarRootModernization.assertInLegacyMode(); animateHide(mNotificationIconAreaInner, animate); } public void showNotificationIconArea(boolean animate) { - StatusBarSimpleFragment.assertInLegacyMode(); + StatusBarRootModernization.assertInLegacyMode(); animateShow(mNotificationIconAreaInner, animate); } public void hideOperatorName(boolean animate) { - StatusBarSimpleFragment.assertInLegacyMode(); + StatusBarRootModernization.assertInLegacyMode(); if (mOperatorNameViewController != null) { animateHide(mOperatorNameViewController.getView(), animate); } } public void showOperatorName(boolean animate) { - StatusBarSimpleFragment.assertInLegacyMode(); + StatusBarRootModernization.assertInLegacyMode(); if (mOperatorNameViewController != null) { animateShow(mOperatorNameViewController.getView(), animate); } @@ -882,7 +886,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue * Animate a view to INVISIBLE or GONE */ private void animateHiddenState(final View v, int state, boolean animate) { - StatusBarSimpleFragment.assertInLegacyMode(); + StatusBarRootModernization.assertInLegacyMode(); v.animate().cancel(); if (!animate || !mAnimationsEnabled) { v.setAlpha(0f); @@ -902,7 +906,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue * Hides a view. */ private void animateHide(final View v, boolean animate) { - StatusBarSimpleFragment.assertInLegacyMode(); + StatusBarRootModernization.assertInLegacyMode(); animateHiddenState(v, View.INVISIBLE, animate); } @@ -910,7 +914,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue * Shows a view, and synchronizes the animation with Keyguard exit animations, if applicable. */ private void animateShow(View v, boolean animate) { - StatusBarSimpleFragment.assertInLegacyMode(); + StatusBarRootModernization.assertInLegacyMode(); v.animate().cancel(); v.setVisibility(View.VISIBLE); if (!animate || !mAnimationsEnabled) { @@ -949,7 +953,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue mOperatorNameViewController.init(); // This view should not be visible on lock-screen if (mKeyguardStateController.isShowing()) { - if (!StatusBarSimpleFragment.isEnabled()) { + if (!StatusBarRootModernization.isEnabled()) { hideOperatorName(false); } } @@ -957,7 +961,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue } private void initOngoingCallChip() { - if (!StatusBarSimpleFragment.isEnabled()) { + if (!StatusBarRootModernization.isEnabled()) { mOngoingCallController.addCallback(mOngoingCallListener); } // TODO(b/364653005): Do we also need to set the secondary activity chip? @@ -969,7 +973,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue @Override public void onDozingChanged(boolean isDozing) { - if (StatusBarSimpleFragment.isEnabled()) { + if (StatusBarRootModernization.isEnabled()) { return; } updateStatusBarVisibilities(/* animate= */ false); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/MultiSourceMinAlphaController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/MultiSourceMinAlphaController.kt index eaf15a8cbe17..e4929b574bd1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/MultiSourceMinAlphaController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/MultiSourceMinAlphaController.kt @@ -20,7 +20,7 @@ import android.view.View import androidx.core.animation.Interpolator import androidx.core.animation.ValueAnimator import com.android.app.animation.InterpolatorsAndroidX -import com.android.systemui.statusbar.core.StatusBarSimpleFragment +import com.android.systemui.statusbar.core.StatusBarRootModernization /** * A controller that keeps track of multiple sources applying alpha value changes to a view. It will @@ -75,7 +75,7 @@ constructor(private val view: View, private val initialAlpha: Float = 1f) { private fun applyAlphaToView() { val minAlpha = getMinAlpha() - if (!StatusBarSimpleFragment.isEnabled) { + if (!StatusBarRootModernization.isEnabled) { view.visibility = if (minAlpha != 0f) View.VISIBLE else View.INVISIBLE view.alpha = minAlpha } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarSystemEventAnimator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarSystemEventAnimator.kt index 1f9ea08e602f..fd7bce099d81 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarSystemEventAnimator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarSystemEventAnimator.kt @@ -71,49 +71,87 @@ constructor( override fun onSystemEventAnimationBegin(): Animator { isAnimationRunning = true - val moveOut = - ValueAnimator.ofFloat(0f, 1f).apply { - duration = 23.frames - interpolator = STATUS_BAR_X_MOVE_OUT - addUpdateListener { - onTranslationXChanged(-(translationXIn * animatedValue as Float)) - } - } - val alphaOut = - ValueAnimator.ofFloat(1f, 0f).apply { - duration = 8.frames - interpolator = null - addUpdateListener { onAlphaChanged(animatedValue as Float) } - } - - val animSet = AnimatorSet() - animSet.playTogether(moveOut, alphaOut) - return animSet + return getDefaultStatusBarAnimationForChipEnter( + translationXIn, + onTranslationXChanged, + onAlphaChanged, + ) } override fun onSystemEventAnimationFinish(hasPersistentDot: Boolean): Animator { onTranslationXChanged(translationXOut.toFloat()) - val moveIn = - ValueAnimator.ofFloat(1f, 0f).apply { - duration = 23.frames - startDelay = 7.frames - interpolator = STATUS_BAR_X_MOVE_IN - addUpdateListener { - onTranslationXChanged(translationXOut * animatedValue as Float) + val anim = + getDefaultStatusBarAnimationForChipExit( + translationXOut, + onTranslationXChanged, + onAlphaChanged, + ) + anim.doOnEnd { isAnimationRunning = false } + anim.doOnCancel { isAnimationRunning = false } + + return anim + } + + /** Static definition of these animations so we can use them more easily from view binders */ + companion object { + /** + * Chip: coming in. Animated view: going out. + * + * Implements the exact spec for animating any status bar elements OUT to make space for the + * chip IN animation. + */ + fun getDefaultStatusBarAnimationForChipEnter( + targetTranslation: Int, + setX: (Float) -> Unit, + setAlpha: (Float) -> Unit, + ): Animator { + val moveOut = + ValueAnimator.ofFloat(0f, 1f).apply { + duration = 23.frames + interpolator = STATUS_BAR_X_MOVE_OUT + addUpdateListener { setX(-(targetTranslation * animatedValue as Float)) } + } + val alphaOut = + ValueAnimator.ofFloat(1f, 0f).apply { + duration = 8.frames + interpolator = null + addUpdateListener { setAlpha(animatedValue as Float) } + } + + val animSet = AnimatorSet() + animSet.playTogether(moveOut, alphaOut) + return animSet + } + + /** + * Chip: going out. Animated view: coming in. + * + * Implements the exact spec for animating any status bar elements IN as the chip is + * animating OUT + */ + fun getDefaultStatusBarAnimationForChipExit( + targetTranslation: Int, + setX: (Float) -> Unit, + setAlpha: (Float) -> Unit, + ): Animator { + val moveIn = + ValueAnimator.ofFloat(1f, 0f).apply { + duration = 23.frames + startDelay = 7.frames + interpolator = STATUS_BAR_X_MOVE_IN + addUpdateListener { setX(targetTranslation * animatedValue as Float) } + } + val alphaIn = + ValueAnimator.ofFloat(0f, 1f).apply { + duration = 5.frames + startDelay = 11.frames + interpolator = null + addUpdateListener { setAlpha(animatedValue as Float) } } - } - val alphaIn = - ValueAnimator.ofFloat(0f, 1f).apply { - duration = 5.frames - startDelay = 11.frames - interpolator = null - addUpdateListener { onAlphaChanged(animatedValue as Float) } - } - val animatorSet = AnimatorSet() - animatorSet.playTogether(moveIn, alphaIn) - animatorSet.doOnEnd { isAnimationRunning = false } - animatorSet.doOnCancel { isAnimationRunning = false } - return animatorSet + val animatorSet = AnimatorSet() + animatorSet.playTogether(moveIn, alphaIn) + return animatorSet + } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt index 935b1012be31..bfdc8bd5e4c8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt @@ -23,6 +23,8 @@ import com.android.systemui.log.LogBuffer import com.android.systemui.log.LogBufferFactory import com.android.systemui.log.table.TableLogBuffer import com.android.systemui.log.table.TableLogBufferFactory +import com.android.systemui.statusbar.events.data.repository.SystemStatusEventAnimationRepository +import com.android.systemui.statusbar.events.data.repository.SystemStatusEventAnimationRepositoryImpl import com.android.systemui.statusbar.pipeline.airplane.data.repository.AirplaneModeRepository import com.android.systemui.statusbar.pipeline.airplane.data.repository.AirplaneModeRepositoryImpl import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModel @@ -85,6 +87,11 @@ abstract class StatusBarPipelineModule { abstract fun connectivityRepository(impl: ConnectivityRepositoryImpl): ConnectivityRepository @Binds + abstract fun systemStatusEventAnimationRepository( + impl: SystemStatusEventAnimationRepositoryImpl + ): SystemStatusEventAnimationRepository + + @Binds abstract fun realDeviceBasedSatelliteRepository( impl: DeviceBasedSatelliteRepositoryImpl ): RealDeviceBasedSatelliteRepository diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt index 9cbfc440ab16..94e9d26c9dc8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt @@ -21,6 +21,7 @@ import android.telephony.ServiceState import android.telephony.SignalStrength import android.telephony.TelephonyDisplayInfo import android.telephony.TelephonyManager +import android.telephony.satellite.NtnSignalStrength import com.android.settingslib.SignalIcon import com.android.settingslib.mobile.MobileMappings import com.android.systemui.dagger.SysUISingleton @@ -31,11 +32,7 @@ import javax.inject.Inject /** Logs for inputs into the mobile pipeline. */ @SysUISingleton -class MobileInputLogger -@Inject -constructor( - @MobileInputLog private val buffer: LogBuffer, -) { +class MobileInputLogger @Inject constructor(@MobileInputLog private val buffer: LogBuffer) { fun logOnServiceStateChanged(serviceState: ServiceState, subId: Int) { buffer.log( TAG, @@ -49,7 +46,7 @@ constructor( { "onServiceStateChanged: subId=$int1 emergencyOnly=$bool1 roaming=$bool2" + " operator=$str1" - } + }, ) } @@ -61,7 +58,7 @@ constructor( int1 = subId bool1 = serviceState.isEmergencyOnly }, - { "ACTION_SERVICE_STATE for subId=$int1. ServiceState.isEmergencyOnly=$bool1" } + { "ACTION_SERVICE_STATE for subId=$int1. ServiceState.isEmergencyOnly=$bool1" }, ) } @@ -70,7 +67,7 @@ constructor( TAG, LogLevel.INFO, { int1 = subId }, - { "ACTION_SERVICE_STATE for subId=$int1. Intent is missing extras. Ignoring" } + { "ACTION_SERVICE_STATE for subId=$int1. Intent is missing extras. Ignoring" }, ) } @@ -82,7 +79,16 @@ constructor( int1 = subId str1 = signalStrength.toString() }, - { "onSignalStrengthsChanged: subId=$int1 strengths=$str1" } + { "onSignalStrengthsChanged: subId=$int1 strengths=$str1" }, + ) + } + + fun logNtnSignalStrengthChanged(signalStrength: NtnSignalStrength) { + buffer.log( + TAG, + LogLevel.INFO, + { int1 = signalStrength.level }, + { "onCarrierRoamingNtnSignalStrengthChanged: level=$int1" }, ) } @@ -128,7 +134,7 @@ constructor( TAG, LogLevel.INFO, { bool1 = active }, - { "onCarrierRoamingNtnModeChanged: $bool1" } + { "onCarrierRoamingNtnModeChanged: $bool1" }, ) } @@ -146,12 +152,7 @@ constructor( } fun logCarrierConfigChanged(subId: Int) { - buffer.log( - TAG, - LogLevel.INFO, - { int1 = subId }, - { "onCarrierConfigChanged: subId=$int1" }, - ) + buffer.log(TAG, LogLevel.INFO, { int1 = subId }, { "onCarrierConfigChanged: subId=$int1" }) } fun logOnDataEnabledChanged(enabled: Boolean, subId: Int) { @@ -175,7 +176,7 @@ constructor( TAG, LogLevel.INFO, { str1 = config.toString() }, - { "defaultDataSubRatConfig: $str1" } + { "defaultDataSubRatConfig: $str1" }, ) } @@ -184,7 +185,7 @@ constructor( TAG, LogLevel.INFO, { str1 = mapping.toString() }, - { "defaultMobileIconMapping: $str1" } + { "defaultMobileIconMapping: $str1" }, ) } @@ -216,7 +217,7 @@ constructor( { "Intent: ACTION_SERVICE_PROVIDERS_UPDATED." + " showSpn=$bool1 spn=$str1 dataSpn=$str2 showPlmn=$bool2 plmn=$str3" - } + }, ) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt index 205205eac210..07843f1ef041 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt @@ -107,6 +107,12 @@ interface MobileConnectionRepository { // @IntRange(from = 0, to = 4) val primaryLevel: StateFlow<Int> + /** + * This level can be used to reflect the signal strength when in carrier roaming NTN mode + * (carrier-based satellite) + */ + val satelliteLevel: StateFlow<Int> + /** The current data connection state. See [DataConnectionState] */ val dataConnectionState: StateFlow<DataConnectionState> diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionRepository.kt index 3261b71ece3c..be3977ecd4ba 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionRepository.kt @@ -37,12 +37,14 @@ import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.FullM import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.FullMobileConnectionRepository.Companion.COL_OPERATOR import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.FullMobileConnectionRepository.Companion.COL_PRIMARY_LEVEL import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.FullMobileConnectionRepository.Companion.COL_ROAMING +import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.FullMobileConnectionRepository.Companion.COL_SATELLITE_LEVEL import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel import com.android.systemui.statusbar.pipeline.shared.data.model.toMobileDataActivityModel import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.model.FakeWifiEventModel import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn @@ -75,7 +77,7 @@ class DemoMobileConnectionRepository( tableLogBuffer, columnPrefix = "", columnName = "inflate", - _inflateSignalStrength.value + _inflateSignalStrength.value, ) .stateIn(scope, SharingStarted.WhileSubscribed(), _inflateSignalStrength.value) @@ -89,7 +91,7 @@ class DemoMobileConnectionRepository( tableLogBuffer, columnPrefix = "", columnName = COL_EMERGENCY, - _isEmergencyOnly.value + _isEmergencyOnly.value, ) .stateIn(scope, SharingStarted.WhileSubscribed(), _isEmergencyOnly.value) @@ -100,7 +102,7 @@ class DemoMobileConnectionRepository( tableLogBuffer, columnPrefix = "", columnName = COL_ROAMING, - _isRoaming.value + _isRoaming.value, ) .stateIn(scope, SharingStarted.WhileSubscribed(), _isRoaming.value) @@ -111,7 +113,7 @@ class DemoMobileConnectionRepository( tableLogBuffer, columnPrefix = "", columnName = COL_OPERATOR, - _operatorAlphaShort.value + _operatorAlphaShort.value, ) .stateIn(scope, SharingStarted.WhileSubscribed(), _operatorAlphaShort.value) @@ -122,7 +124,7 @@ class DemoMobileConnectionRepository( tableLogBuffer, columnPrefix = "", columnName = COL_IS_IN_SERVICE, - _isInService.value + _isInService.value, ) .stateIn(scope, SharingStarted.WhileSubscribed(), _isInService.value) @@ -133,7 +135,7 @@ class DemoMobileConnectionRepository( tableLogBuffer, columnPrefix = "", columnName = COL_IS_NTN, - _isNonTerrestrial.value + _isNonTerrestrial.value, ) .stateIn(scope, SharingStarted.WhileSubscribed(), _isNonTerrestrial.value) @@ -144,7 +146,7 @@ class DemoMobileConnectionRepository( tableLogBuffer, columnPrefix = "", columnName = COL_IS_GSM, - _isGsm.value + _isGsm.value, ) .stateIn(scope, SharingStarted.WhileSubscribed(), _isGsm.value) @@ -155,7 +157,7 @@ class DemoMobileConnectionRepository( tableLogBuffer, columnPrefix = "", columnName = COL_CDMA_LEVEL, - _cdmaLevel.value + _cdmaLevel.value, ) .stateIn(scope, SharingStarted.WhileSubscribed(), _cdmaLevel.value) @@ -166,10 +168,21 @@ class DemoMobileConnectionRepository( tableLogBuffer, columnPrefix = "", columnName = COL_PRIMARY_LEVEL, - _primaryLevel.value + _primaryLevel.value, ) .stateIn(scope, SharingStarted.WhileSubscribed(), _primaryLevel.value) + private val _satelliteLevel = MutableStateFlow(0) + override val satelliteLevel: StateFlow<Int> = + _satelliteLevel + .logDiffsForTable( + tableLogBuffer, + columnPrefix = "", + columnName = COL_SATELLITE_LEVEL, + _satelliteLevel.value, + ) + .stateIn(scope, SharingStarted.WhileSubscribed(), _satelliteLevel.value) + private val _dataConnectionState = MutableStateFlow(DataConnectionState.Disconnected) override val dataConnectionState = _dataConnectionState @@ -177,12 +190,7 @@ class DemoMobileConnectionRepository( .stateIn(scope, SharingStarted.WhileSubscribed(), _dataConnectionState.value) private val _dataActivityDirection = - MutableStateFlow( - DataActivityModel( - hasActivityIn = false, - hasActivityOut = false, - ) - ) + MutableStateFlow(DataActivityModel(hasActivityIn = false, hasActivityOut = false)) override val dataActivityDirection = _dataActivityDirection .logDiffsForTable(tableLogBuffer, columnPrefix = "", _dataActivityDirection.value) @@ -195,7 +203,7 @@ class DemoMobileConnectionRepository( tableLogBuffer, columnPrefix = "", columnName = COL_CARRIER_NETWORK_CHANGE, - _carrierNetworkChangeActive.value + _carrierNetworkChangeActive.value, ) .stateIn(scope, SharingStarted.WhileSubscribed(), _carrierNetworkChangeActive.value) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepository.kt index 2e4767893c3d..75f613d7e3c6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepository.kt @@ -90,7 +90,7 @@ class CarrierMergedConnectionRepository( TAG, "Connection repo subId=$subId " + "does not equal wifi repo subId=${network.subscriptionId}; " + - "not showing carrier merged" + "not showing carrier merged", ) null } @@ -149,7 +149,7 @@ class CarrierMergedConnectionRepository( .stateIn( scope, SharingStarted.WhileSubscribed(), - ResolvedNetworkType.UnknownNetworkType + ResolvedNetworkType.UnknownNetworkType, ) override val dataConnectionState = @@ -173,6 +173,7 @@ class CarrierMergedConnectionRepository( override val isNonTerrestrial = MutableStateFlow(false).asStateFlow() override val isGsm = MutableStateFlow(false).asStateFlow() override val carrierNetworkChangeActive = MutableStateFlow(false).asStateFlow() + override val satelliteLevel = MutableStateFlow(0) /** * Carrier merged connections happen over wifi but are displayed as a mobile triangle. Because @@ -207,10 +208,7 @@ class CarrierMergedConnectionRepository( @Application private val scope: CoroutineScope, private val wifiRepository: WifiRepository, ) { - fun build( - subId: Int, - mobileLogger: TableLogBuffer, - ): MobileConnectionRepository { + fun build(subId: Int, mobileLogger: TableLogBuffer): MobileConnectionRepository { return CarrierMergedConnectionRepository( subId, mobileLogger, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt index a5e47a6e68cd..fae9be083e12 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt @@ -132,12 +132,12 @@ class FullMobileConnectionRepository( tableLogBuffer, columnPrefix = "", columnName = COL_EMERGENCY, - activeRepo.value.isEmergencyOnly.value + activeRepo.value.isEmergencyOnly.value, ) .stateIn( scope, SharingStarted.WhileSubscribed(), - activeRepo.value.isEmergencyOnly.value + activeRepo.value.isEmergencyOnly.value, ) override val isRoaming = @@ -147,7 +147,7 @@ class FullMobileConnectionRepository( tableLogBuffer, columnPrefix = "", columnName = COL_ROAMING, - activeRepo.value.isRoaming.value + activeRepo.value.isRoaming.value, ) .stateIn(scope, SharingStarted.WhileSubscribed(), activeRepo.value.isRoaming.value) @@ -158,12 +158,12 @@ class FullMobileConnectionRepository( tableLogBuffer, columnPrefix = "", columnName = COL_OPERATOR, - activeRepo.value.operatorAlphaShort.value + activeRepo.value.operatorAlphaShort.value, ) .stateIn( scope, SharingStarted.WhileSubscribed(), - activeRepo.value.operatorAlphaShort.value + activeRepo.value.operatorAlphaShort.value, ) override val isInService = @@ -173,7 +173,7 @@ class FullMobileConnectionRepository( tableLogBuffer, columnPrefix = "", columnName = COL_IS_IN_SERVICE, - activeRepo.value.isInService.value + activeRepo.value.isInService.value, ) .stateIn(scope, SharingStarted.WhileSubscribed(), activeRepo.value.isInService.value) @@ -184,12 +184,12 @@ class FullMobileConnectionRepository( tableLogBuffer, columnPrefix = "", columnName = COL_IS_NTN, - activeRepo.value.isNonTerrestrial.value + activeRepo.value.isNonTerrestrial.value, ) .stateIn( scope, SharingStarted.WhileSubscribed(), - activeRepo.value.isNonTerrestrial.value + activeRepo.value.isNonTerrestrial.value, ) override val isGsm = @@ -199,7 +199,7 @@ class FullMobileConnectionRepository( tableLogBuffer, columnPrefix = "", columnName = COL_IS_GSM, - activeRepo.value.isGsm.value + activeRepo.value.isGsm.value, ) .stateIn(scope, SharingStarted.WhileSubscribed(), activeRepo.value.isGsm.value) @@ -210,7 +210,7 @@ class FullMobileConnectionRepository( tableLogBuffer, columnPrefix = "", columnName = COL_CDMA_LEVEL, - activeRepo.value.cdmaLevel.value + activeRepo.value.cdmaLevel.value, ) .stateIn(scope, SharingStarted.WhileSubscribed(), activeRepo.value.cdmaLevel.value) @@ -221,22 +221,33 @@ class FullMobileConnectionRepository( tableLogBuffer, columnPrefix = "", columnName = COL_PRIMARY_LEVEL, - activeRepo.value.primaryLevel.value + activeRepo.value.primaryLevel.value, ) .stateIn(scope, SharingStarted.WhileSubscribed(), activeRepo.value.primaryLevel.value) + override val satelliteLevel: StateFlow<Int> = + activeRepo + .flatMapLatest { it.satelliteLevel } + .logDiffsForTable( + tableLogBuffer, + columnPrefix = "", + columnName = COL_SATELLITE_LEVEL, + activeRepo.value.satelliteLevel.value, + ) + .stateIn(scope, SharingStarted.WhileSubscribed(), activeRepo.value.satelliteLevel.value) + override val dataConnectionState = activeRepo .flatMapLatest { it.dataConnectionState } .logDiffsForTable( tableLogBuffer, columnPrefix = "", - activeRepo.value.dataConnectionState.value + activeRepo.value.dataConnectionState.value, ) .stateIn( scope, SharingStarted.WhileSubscribed(), - activeRepo.value.dataConnectionState.value + activeRepo.value.dataConnectionState.value, ) override val dataActivityDirection = @@ -245,12 +256,12 @@ class FullMobileConnectionRepository( .logDiffsForTable( tableLogBuffer, columnPrefix = "", - activeRepo.value.dataActivityDirection.value + activeRepo.value.dataActivityDirection.value, ) .stateIn( scope, SharingStarted.WhileSubscribed(), - activeRepo.value.dataActivityDirection.value + activeRepo.value.dataActivityDirection.value, ) override val carrierNetworkChangeActive = @@ -260,12 +271,12 @@ class FullMobileConnectionRepository( tableLogBuffer, columnPrefix = "", columnName = COL_CARRIER_NETWORK_CHANGE, - activeRepo.value.carrierNetworkChangeActive.value + activeRepo.value.carrierNetworkChangeActive.value, ) .stateIn( scope, SharingStarted.WhileSubscribed(), - activeRepo.value.carrierNetworkChangeActive.value + activeRepo.value.carrierNetworkChangeActive.value, ) override val resolvedNetworkType = @@ -274,12 +285,12 @@ class FullMobileConnectionRepository( .logDiffsForTable( tableLogBuffer, columnPrefix = "", - activeRepo.value.resolvedNetworkType.value + activeRepo.value.resolvedNetworkType.value, ) .stateIn( scope, SharingStarted.WhileSubscribed(), - activeRepo.value.resolvedNetworkType.value + activeRepo.value.resolvedNetworkType.value, ) override val dataEnabled = @@ -305,7 +316,7 @@ class FullMobileConnectionRepository( .stateIn( scope, SharingStarted.WhileSubscribed(), - activeRepo.value.inflateSignalStrength.value + activeRepo.value.inflateSignalStrength.value, ) override val allowNetworkSliceIndicator = @@ -320,7 +331,7 @@ class FullMobileConnectionRepository( .stateIn( scope, SharingStarted.WhileSubscribed(), - activeRepo.value.allowNetworkSliceIndicator.value + activeRepo.value.allowNetworkSliceIndicator.value, ) override val numberOfLevels = @@ -439,6 +450,7 @@ class FullMobileConnectionRepository( const val COL_IS_IN_SERVICE = "isInService" const val COL_OPERATOR = "operatorName" const val COL_PRIMARY_LEVEL = "primaryLevel" + const val COL_SATELLITE_LEVEL = "satelliteLevel" const val COL_ROAMING = "roaming" } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt index 62bd8ad4317c..8a1e7f9a0096 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt @@ -41,6 +41,7 @@ import android.telephony.TelephonyManager.ERI_ON import android.telephony.TelephonyManager.EXTRA_SUBSCRIPTION_ID import android.telephony.TelephonyManager.NETWORK_TYPE_UNKNOWN import android.telephony.TelephonyManager.UNKNOWN_CARRIER_ID +import android.telephony.satellite.NtnSignalStrength import com.android.settingslib.Utils import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow @@ -173,7 +174,7 @@ class MobileConnectionRepositoryImpl( override fun onDataConnectionStateChanged( dataState: Int, - networkType: Int + networkType: Int, ) { logger.logOnDataConnectionStateChanged(dataState, networkType, subId) trySend(CallbackEvent.OnDataConnectionStateChanged(dataState)) @@ -195,6 +196,17 @@ class MobileConnectionRepositoryImpl( logger.logOnSignalStrengthsChanged(signalStrength, subId) trySend(CallbackEvent.OnSignalStrengthChanged(signalStrength)) } + + override fun onCarrierRoamingNtnSignalStrengthChanged( + signalStrength: NtnSignalStrength + ) { + logger.logNtnSignalStrengthChanged(signalStrength) + trySend( + CallbackEvent.OnCarrierRoamingNtnSignalStrengthChanged( + signalStrength + ) + ) + } } telephonyManager.registerTelephonyCallback(bgDispatcher.asExecutor(), callback) awaitClose { telephonyManager.unregisterTelephonyCallback(callback) } @@ -267,6 +279,12 @@ class MobileConnectionRepositoryImpl( .map { it.signalStrength.level } .stateIn(scope, SharingStarted.WhileSubscribed(), SIGNAL_STRENGTH_NONE_OR_UNKNOWN) + override val satelliteLevel: StateFlow<Int> = + callbackEvents + .mapNotNull { it.onCarrierRoamingNtnSignalStrengthChanged } + .map { it.signalStrength.level } + .stateIn(scope, SharingStarted.WhileSubscribed(), 0) + override val dataConnectionState = callbackEvents .mapNotNull { it.onDataConnectionStateChanged } @@ -280,7 +298,7 @@ class MobileConnectionRepositoryImpl( .stateIn( scope, SharingStarted.WhileSubscribed(), - DataActivityModel(hasActivityIn = false, hasActivityOut = false) + DataActivityModel(hasActivityIn = false, hasActivityOut = false), ) override val carrierNetworkChangeActive = @@ -385,7 +403,7 @@ class MobileConnectionRepositoryImpl( if ( intent.getIntExtra( EXTRA_SUBSCRIPTION_INDEX, - INVALID_SUBSCRIPTION_ID + INVALID_SUBSCRIPTION_ID, ) == subId ) { logger.logServiceProvidersUpdatedBroadcast(intent) @@ -399,7 +417,7 @@ class MobileConnectionRepositoryImpl( context.registerReceiver( receiver, - IntentFilter(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED) + IntentFilter(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED), ) awaitClose { context.unregisterReceiver(receiver) } @@ -524,6 +542,9 @@ sealed interface CallbackEvent { data class OnServiceStateChanged(val serviceState: ServiceState) : CallbackEvent data class OnSignalStrengthChanged(val signalStrength: SignalStrength) : CallbackEvent + + data class OnCarrierRoamingNtnSignalStrengthChanged(val signalStrength: NtnSignalStrength) : + CallbackEvent } /** @@ -539,6 +560,9 @@ data class TelephonyCallbackState( val onDisplayInfoChanged: CallbackEvent.OnDisplayInfoChanged? = null, val onServiceStateChanged: CallbackEvent.OnServiceStateChanged? = null, val onSignalStrengthChanged: CallbackEvent.OnSignalStrengthChanged? = null, + val onCarrierRoamingNtnSignalStrengthChanged: + CallbackEvent.OnCarrierRoamingNtnSignalStrengthChanged? = + null, ) { fun applyEvent(event: CallbackEvent): TelephonyCallbackState { return when (event) { @@ -555,6 +579,8 @@ data class TelephonyCallbackState( copy(onServiceStateChanged = event) } is CallbackEvent.OnSignalStrengthChanged -> copy(onSignalStrengthChanged = event) + is CallbackEvent.OnCarrierRoamingNtnSignalStrengthChanged -> + copy(onCarrierRoamingNtnSignalStrengthChanged = event) } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt index 4ef328cf1623..1bf14af7ea6e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt @@ -335,7 +335,11 @@ class MobileIconInteractorImpl( // Satellite level is unaffected by the inflateSignalStrength property // See b/346904529 for details private val satelliteShownLevel: StateFlow<Int> = - combine(level, isInService) { level, isInService -> if (isInService) level else 0 } + if (Flags.carrierRoamingNbIotNtn()) { + connectionRepository.satelliteLevel + } else { + combine(level, isInService) { level, isInService -> if (isInService) level else 0 } + } .stateIn(scope, SharingStarted.WhileSubscribed(), 0) private val cellularIcon: Flow<SignalIconModel.Cellular> = diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt index 11d73397ca22..72df02714d43 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt @@ -22,6 +22,7 @@ import android.view.View 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.Flags import com.android.systemui.dagger.SysUISingleton import com.android.systemui.lifecycle.repeatWhenAttached @@ -30,13 +31,16 @@ import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips import com.android.systemui.statusbar.chips.ui.binder.OngoingActivityChipBinder import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel -import com.android.systemui.statusbar.core.StatusBarSimpleFragment +import com.android.systemui.statusbar.core.StatusBarRootModernization +import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState +import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState.AnimatingIn +import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState.AnimatingOut +import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState.RunningChipAnim import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.HomeStatusBarViewModel import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.HomeStatusBarViewModel.VisibilityModel import javax.inject.Inject -import com.android.app.tracing.coroutines.launchTraced as launch /** * Interface to assist with binding the [CollapsedStatusBarFragment] to [HomeStatusBarViewModel]. @@ -46,10 +50,16 @@ interface HomeStatusBarViewBinder { /** * Binds the view to the view-model. [listener] will be notified whenever an event that may * change the status bar visibility occurs. + * + * Null chip animations are used when [StatusBarRootModernization] is off (i.e., when we are + * binding from the fragment). If non-null, they control the animation of the system icon area + * to support the chip animations. */ fun bind( view: View, viewModel: HomeStatusBarViewModel, + systemEventChipAnimateIn: ((View) -> Unit)?, + systemEventChipAnimateOut: ((View) -> Unit)?, listener: StatusBarVisibilityChangeListener, ) } @@ -59,6 +69,8 @@ class HomeStatusBarViewBinderImpl @Inject constructor() : HomeStatusBarViewBinde override fun bind( view: View, viewModel: HomeStatusBarViewModel, + systemEventChipAnimateIn: ((View) -> Unit)?, + systemEventChipAnimateOut: ((View) -> Unit)?, listener: StatusBarVisibilityChangeListener, ) { view.repeatWhenAttached { @@ -91,10 +103,11 @@ class HomeStatusBarViewBinderImpl @Inject constructor() : HomeStatusBarViewBinde launch { viewModel.primaryOngoingActivityChip.collect { primaryChipModel -> OngoingActivityChipBinder.bind(primaryChipModel, primaryChipView) - if (StatusBarSimpleFragment.isEnabled) { + if (StatusBarRootModernization.isEnabled) { when (primaryChipModel) { is OngoingActivityChipModel.Shown -> primaryChipView.show(shouldAnimateChange = true) + is OngoingActivityChipModel.Hidden -> primaryChipView.hide( state = View.GONE, @@ -109,6 +122,7 @@ class HomeStatusBarViewBinderImpl @Inject constructor() : HomeStatusBarViewBinde hasSecondaryOngoingActivity = false, shouldAnimate = true, ) + is OngoingActivityChipModel.Hidden -> listener.onOngoingActivityStatusChanged( hasPrimaryOngoingActivity = false, @@ -133,7 +147,7 @@ class HomeStatusBarViewBinderImpl @Inject constructor() : HomeStatusBarViewBinde // enough space for it. OngoingActivityChipBinder.bind(chips.secondary, secondaryChipView) - if (StatusBarSimpleFragment.isEnabled) { + if (StatusBarRootModernization.isEnabled) { primaryChipView.adjustVisibility(chips.primary.toVisibilityModel()) secondaryChipView.adjustVisibility( chips.secondary.toVisibilityModel() @@ -160,7 +174,7 @@ class HomeStatusBarViewBinderImpl @Inject constructor() : HomeStatusBarViewBinde } } - if (StatusBarSimpleFragment.isEnabled) { + if (StatusBarRootModernization.isEnabled) { val clockView = view.requireViewById<View>(R.id.clock) launch { viewModel.isClockVisible.collect { clockView.adjustVisibility(it) } } @@ -175,10 +189,30 @@ class HomeStatusBarViewBinderImpl @Inject constructor() : HomeStatusBarViewBinde view.requireViewById<View>(R.id.status_bar_end_side_content) // TODO(b/364360986): Also handle operator name view. launch { - viewModel.isSystemInfoVisible.collect { - systemInfoView.adjustVisibility(it) - // TODO(b/364360986): The system info view has a custom alpha controller - // in CollapsedStatusBarFragment. + viewModel.systemInfoCombinedVis.collect { (baseVis, animState) -> + // Broadly speaking, the baseVis controls the view.visibility, and + // the animation state uses only alpha to achieve its effect. This + // means that we can always modify the visibility, and if we're + // animating we can use the animState to handle it. If we are not + // animating, then we can use the baseVis default animation + if (animState.isAnimatingChip()) { + // Just apply the visibility of the view, but don't animate + systemInfoView.visibility = baseVis.visibility + // Now apply the animation state, with its animator + when (animState) { + AnimatingIn -> { + systemEventChipAnimateIn?.invoke(systemInfoView) + } + AnimatingOut -> { + systemEventChipAnimateOut?.invoke(systemInfoView) + } + else -> { + // Nothing to do here + } + } + } else { + systemInfoView.adjustVisibility(baseVis) + } } } } @@ -186,6 +220,14 @@ class HomeStatusBarViewBinderImpl @Inject constructor() : HomeStatusBarViewBinde } } + private fun SystemEventAnimationState.isAnimatingChip() = + when (this) { + AnimatingIn, + AnimatingOut, + RunningChipAnim -> true + else -> false + } + private fun OngoingActivityChipModel.toVisibilityModel(): VisibilityModel { return VisibilityModel( visibility = if (this is OngoingActivityChipModel.Shown) View.VISIBLE else View.GONE, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt index 247abc3da175..1faa9f32af1f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt @@ -35,6 +35,7 @@ import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.plugins.DarkIconDispatcher import com.android.systemui.res.R import com.android.systemui.statusbar.data.repository.DarkIconDispatcherStore +import com.android.systemui.statusbar.events.domain.interactor.SystemStatusEventAnimationInteractor import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerStatusBarViewBinder import com.android.systemui.statusbar.phone.NotificationIconContainer import com.android.systemui.statusbar.phone.PhoneStatusBarView @@ -59,6 +60,7 @@ constructor( private val iconController: StatusBarIconController, private val ongoingCallController: OngoingCallController, private val darkIconDispatcherStore: DarkIconDispatcherStore, + private val eventAnimationInteractor: SystemStatusEventAnimationInteractor, ) { fun create(root: ViewGroup, andThen: (ViewGroup) -> Unit): ComposeView { val composeView = ComposeView(root.context) @@ -73,6 +75,7 @@ constructor( iconController = iconController, ongoingCallController = ongoingCallController, darkIconDispatcher = darkIconDispatcherStore.forDisplay(root.context.displayId), + eventAnimationInteractor = eventAnimationInteractor, onViewCreated = andThen, ) } @@ -102,9 +105,10 @@ fun StatusBarRoot( iconController: StatusBarIconController, ongoingCallController: OngoingCallController, darkIconDispatcher: DarkIconDispatcher, + eventAnimationInteractor: SystemStatusEventAnimationInteractor, onViewCreated: (ViewGroup) -> Unit, ) { - // None of these methods are used when [StatusBarSimpleFragment] is on. + // None of these methods are used when [StatusBarRootModernization] is on. // This can be deleted once the fragment is gone val nopVisibilityChangeListener = object : StatusBarVisibilityChangeListener { @@ -181,6 +185,8 @@ fun StatusBarRoot( statusBarViewBinder.bind( phoneStatusBarView, statusBarViewModel, + eventAnimationInteractor::animateStatusBarContentForChipEnter, + eventAnimationInteractor::animateStatusBarContentForChipExit, nopVisibilityChangeListener, ) } 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 4277a8be64d3..6a9b43c995e4 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 @@ -35,6 +35,9 @@ import com.android.systemui.shade.domain.interactor.ShadeInteractor 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 +import com.android.systemui.statusbar.events.domain.interactor.SystemStatusEventAnimationInteractor +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.shared.NotificationsLiveDataStoreRefactor import com.android.systemui.statusbar.phone.domain.interactor.LightsOutInteractor @@ -95,7 +98,12 @@ interface HomeStatusBarViewModel { val isClockVisible: Flow<VisibilityModel> val isNotificationIconContainerVisible: Flow<VisibilityModel> - val isSystemInfoVisible: Flow<VisibilityModel> + /** + * Pair of (system info visibility, event animation state). The animation state can be used to + * respond to the system event chip animations. In all cases, system info visibility correctly + * models the View.visibility for the system info area + */ + val systemInfoCombinedVis: StateFlow<SystemInfoCombinedVisibilityModel> /** * Apps can request a low profile mode [android.view.View.SYSTEM_UI_FLAG_LOW_PROFILE] where @@ -114,6 +122,12 @@ interface HomeStatusBarViewModel { /** True if a visibility change should be animated. */ val shouldAnimateChange: Boolean, ) + + /** The combined visibility + animation state for the system info status bar area */ + data class SystemInfoCombinedVisibilityModel( + val baseVisibility: VisibilityModel, + val animationState: SystemEventAnimationState, + ) } @SysUISingleton @@ -129,6 +143,7 @@ constructor( sceneContainerOcclusionInteractor: SceneContainerOcclusionInteractor, shadeInteractor: ShadeInteractor, ongoingActivityChipsViewModel: OngoingActivityChipsViewModel, + animations: SystemStatusEventAnimationInteractor, @Application coroutineScope: CoroutineScope, ) : HomeStatusBarViewModel { override val isTransitioningFromLockscreenToOccluded: StateFlow<Boolean> = @@ -228,7 +243,7 @@ constructor( visibilityViaDisableFlags.animate, ) } - override val isSystemInfoVisible: Flow<VisibilityModel> = + private val isSystemInfoVisible = combine( shouldHomeStatusBarBeVisible, collapsedStatusBarInteractor.visibilityViaDisableFlags, @@ -238,6 +253,22 @@ constructor( VisibilityModel(showSystemInfo.toVisibilityInt(), visibilityViaDisableFlags.animate) } + override val systemInfoCombinedVis = + combine(isSystemInfoVisible, animations.animationState) { sysInfoVisible, animationState -> + HomeStatusBarViewModel.SystemInfoCombinedVisibilityModel( + sysInfoVisible, + animationState, + ) + } + .stateIn( + coroutineScope, + SharingStarted.WhileSubscribed(), + HomeStatusBarViewModel.SystemInfoCombinedVisibilityModel( + VisibilityModel(View.VISIBLE, false), + Idle, + ), + ) + @View.Visibility private fun Boolean.toVisibilityInt(): Int { return if (this) View.VISIBLE else View.GONE diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt index 76024cd565d1..c7b6be3fc4ac 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt @@ -17,12 +17,15 @@ package com.android.systemui.statusbar.pipeline.wifi.data.repository.prod import android.annotation.SuppressLint +import android.content.Context import android.net.wifi.ScanResult import android.net.wifi.WifiManager +import android.os.UserHandle import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleRegistry import com.android.internal.annotations.VisibleForTesting +import com.android.systemui.Flags.multiuserWifiPickerTrackerSupport import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application @@ -43,6 +46,7 @@ import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiReposito import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel.Unavailable.toHotspotDeviceType import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiScanEntry +import com.android.systemui.user.data.repository.UserRepository import com.android.wifitrackerlib.HotspotNetworkEntry import com.android.wifitrackerlib.MergedCarrierEntry import com.android.wifitrackerlib.WifiEntry @@ -53,10 +57,12 @@ import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.asExecutor import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn @@ -68,6 +74,8 @@ import kotlinx.coroutines.flow.stateIn class WifiRepositoryImpl @Inject constructor( + @Application applicationContext: Context, + private val userRepository: UserRepository, @Application private val scope: CoroutineScope, @Main private val mainExecutor: Executor, @Background private val bgDispatcher: CoroutineDispatcher, @@ -84,90 +92,226 @@ constructor( private var wifiPickerTracker: WifiPickerTracker? = null - private val wifiPickerTrackerInfo: StateFlow<WifiPickerTrackerInfo> = run { - var current = - WifiPickerTrackerInfo( - state = WIFI_STATE_DEFAULT, - isDefault = false, - primaryNetwork = WIFI_NETWORK_DEFAULT, - secondaryNetworks = emptyList(), - ) - callbackFlow { - val callback = - object : WifiPickerTracker.WifiPickerTrackerCallback { - override fun onWifiEntriesChanged() { - val connectedEntry = wifiPickerTracker.mergedOrPrimaryConnection - logOnWifiEntriesChanged(connectedEntry) - - val activeNetworks = wifiPickerTracker?.activeWifiEntries ?: emptyList() - val secondaryNetworks = - activeNetworks - .filter { it != connectedEntry && !it.isPrimaryNetwork } - .map { it.toWifiNetworkModel() } - - // [WifiPickerTracker.connectedWifiEntry] will return the same instance - // but with updated internals. For example, when its validation status - // changes from false to true, the same instance is re-used but with the - // validated field updated. - // - // Because it's the same instance, the flow won't re-emit the value - // (even though the internals have changed). So, we need to transform it - // into our internal model immediately. [toWifiNetworkModel] always - // returns a new instance, so the flow is guaranteed to emit. - send( - newPrimaryNetwork = - connectedEntry?.toPrimaryWifiNetworkModel() - ?: WIFI_NETWORK_DEFAULT, - newSecondaryNetworks = secondaryNetworks, - newIsDefault = connectedEntry?.isDefaultNetwork ?: false, - ) - } + @VisibleForTesting + val selectedUserContext: Flow<Context> = + userRepository.selectedUserInfo.map { + applicationContext.createContextAsUser(UserHandle.of(it.id), /* flags= */ 0) + } - override fun onWifiStateChanged() { - val state = wifiPickerTracker?.wifiState - logOnWifiStateChanged(state) - send(newState = state ?: WIFI_STATE_DEFAULT) - } + var current = + WifiPickerTrackerInfo( + state = WIFI_STATE_DEFAULT, + isDefault = false, + primaryNetwork = WIFI_NETWORK_DEFAULT, + secondaryNetworks = emptyList(), + ) - override fun onNumSavedNetworksChanged() {} - - override fun onNumSavedSubscriptionsChanged() {} - - private fun send( - newState: Int = current.state, - newIsDefault: Boolean = current.isDefault, - newPrimaryNetwork: WifiNetworkModel = current.primaryNetwork, - newSecondaryNetworks: List<WifiNetworkModel> = - current.secondaryNetworks, - ) { - val new = - WifiPickerTrackerInfo( - newState, - newIsDefault, - newPrimaryNetwork, - newSecondaryNetworks, - ) - current = new - trySend(new) + @kotlinx.coroutines.ExperimentalCoroutinesApi + private val wifiPickerTrackerInfo: StateFlow<WifiPickerTrackerInfo> = + if (multiuserWifiPickerTrackerSupport()) { + selectedUserContext + .flatMapLatest { currentContext + -> // flatMapLatest because when selectedUserContext emits a new value, we want + // to + // re-create a whole flow + callbackFlow { + val callback = + object : WifiPickerTracker.WifiPickerTrackerCallback { + override fun onWifiEntriesChanged() { + val connectedEntry = + wifiPickerTracker.mergedOrPrimaryConnection + logOnWifiEntriesChanged(connectedEntry) + + val activeNetworks = + wifiPickerTracker?.activeWifiEntries ?: emptyList() + val secondaryNetworks = + activeNetworks + .filter { + it != connectedEntry && !it.isPrimaryNetwork + } + .map { it.toWifiNetworkModel() } + + // [WifiPickerTracker.connectedWifiEntry] will return the + // same + // instance but with updated internals. For example, when + // its + // validation status changes from false to true, the same + // instance is re-used but with the validated field updated. + // + // Because it's the same instance, the flow won't re-emit + // the + // value (even though the internals have changed). So, we + // need + // to transform it into our internal model immediately. + // [toWifiNetworkModel] always returns a new instance, so + // the + // flow is guaranteed to emit. + send( + newPrimaryNetwork = + connectedEntry?.toPrimaryWifiNetworkModel() + ?: WIFI_NETWORK_DEFAULT, + newSecondaryNetworks = secondaryNetworks, + newIsDefault = connectedEntry?.isDefaultNetwork ?: false, + ) + } + + override fun onWifiStateChanged() { + val state = wifiPickerTracker?.wifiState + logOnWifiStateChanged(state) + send(newState = state ?: WIFI_STATE_DEFAULT) + } + + override fun onNumSavedNetworksChanged() {} + + override fun onNumSavedSubscriptionsChanged() {} + + private fun send( + newState: Int = current.state, + newIsDefault: Boolean = current.isDefault, + newPrimaryNetwork: WifiNetworkModel = + current.primaryNetwork, + newSecondaryNetworks: List<WifiNetworkModel> = + current.secondaryNetworks, + ) { + val new = + WifiPickerTrackerInfo( + newState, + newIsDefault, + newPrimaryNetwork, + newSecondaryNetworks, + ) + current = new + trySend(new) + } + } + wifiPickerTracker = + wifiPickerTrackerFactory + .create(currentContext, lifecycle, callback, "WifiRepository") + .apply { + // By default, [WifiPickerTracker] will scan to see all + // available wifi networks in the area. Because SysUI only + // needs to display the **connected** network, we don't + // need scans to be running (and in fact, running scans is + // costly and should be avoided whenever possible). + this?.disableScanning() + } + + // The lifecycle must be STARTED in order for the callback to receive + // events. + mainExecutor.execute { + lifecycle.currentState = Lifecycle.State.STARTED + } + awaitClose { + mainExecutor.execute { + lifecycle.currentState = Lifecycle.State.CREATED + } + } } - } + .stateIn(scope, SharingStarted.Eagerly, current) + } + .stateIn(scope, SharingStarted.Eagerly, current) + } else { - wifiPickerTracker = - wifiPickerTrackerFactory.create(lifecycle, callback, "WifiRepository").apply { - // By default, [WifiPickerTracker] will scan to see all available wifi - // networks in the area. Because SysUI only needs to display the - // **connected** network, we don't need scans to be running (and in fact, - // running scans is costly and should be avoided whenever possible). - this?.disableScanning() + run { + var current = + WifiPickerTrackerInfo( + state = WIFI_STATE_DEFAULT, + isDefault = false, + primaryNetwork = WIFI_NETWORK_DEFAULT, + secondaryNetworks = emptyList(), + ) + callbackFlow { + val callback = + object : WifiPickerTracker.WifiPickerTrackerCallback { + override fun onWifiEntriesChanged() { + val connectedEntry = wifiPickerTracker.mergedOrPrimaryConnection + logOnWifiEntriesChanged(connectedEntry) + + val activeNetworks = + wifiPickerTracker?.activeWifiEntries ?: emptyList() + val secondaryNetworks = + activeNetworks + .filter { it != connectedEntry && !it.isPrimaryNetwork } + .map { it.toWifiNetworkModel() } + + // [WifiPickerTracker.connectedWifiEntry] will return the same + // instance + // but with updated internals. For example, when its validation + // status + // changes from false to true, the same instance is re-used but + // with the + // validated field updated. + // + // Because it's the same instance, the flow won't re-emit the + // value + // (even though the internals have changed). So, we need to + // transform it + // into our internal model immediately. [toWifiNetworkModel] + // always + // returns a new instance, so the flow is guaranteed to emit. + send( + newPrimaryNetwork = + connectedEntry?.toPrimaryWifiNetworkModel() + ?: WIFI_NETWORK_DEFAULT, + newSecondaryNetworks = secondaryNetworks, + newIsDefault = connectedEntry?.isDefaultNetwork ?: false, + ) + } + + override fun onWifiStateChanged() { + val state = wifiPickerTracker?.wifiState + logOnWifiStateChanged(state) + send(newState = state ?: WIFI_STATE_DEFAULT) + } + + override fun onNumSavedNetworksChanged() {} + + override fun onNumSavedSubscriptionsChanged() {} + + private fun send( + newState: Int = current.state, + newIsDefault: Boolean = current.isDefault, + newPrimaryNetwork: WifiNetworkModel = current.primaryNetwork, + newSecondaryNetworks: List<WifiNetworkModel> = + current.secondaryNetworks, + ) { + val new = + WifiPickerTrackerInfo( + newState, + newIsDefault, + newPrimaryNetwork, + newSecondaryNetworks, + ) + current = new + trySend(new) + } + } + + wifiPickerTracker = + wifiPickerTrackerFactory + .create(applicationContext, lifecycle, callback, "WifiRepository") + .apply { + // By default, [WifiPickerTracker] will scan to see all + // available wifi + // networks in the area. Because SysUI only needs to display the + // **connected** network, we don't need scans to be running (and + // in fact, + // running scans is costly and should be avoided whenever + // possible). + this?.disableScanning() + } + // The lifecycle must be STARTED in order for the callback to receive + // events. + mainExecutor.execute { lifecycle.currentState = Lifecycle.State.STARTED } + awaitClose { + mainExecutor.execute { + lifecycle.currentState = Lifecycle.State.CREATED + } + } } - // The lifecycle must be STARTED in order for the callback to receive events. - mainExecutor.execute { lifecycle.currentState = Lifecycle.State.STARTED } - awaitClose { - mainExecutor.execute { lifecycle.currentState = Lifecycle.State.CREATED } - } + .stateIn(scope, SharingStarted.Eagerly, current) } - .stateIn(scope, SharingStarted.Eagerly, current) - } + } override val isWifiEnabled: StateFlow<Boolean> = wifiPickerTrackerInfo @@ -185,11 +329,7 @@ constructor( wifiPickerTrackerInfo .map { it.primaryNetwork } .distinctUntilChanged() - .logDiffsForTable( - tableLogger, - columnPrefix = "", - initialValue = WIFI_NETWORK_DEFAULT, - ) + .logDiffsForTable(tableLogger, columnPrefix = "", initialValue = WIFI_NETWORK_DEFAULT) .stateIn(scope, SharingStarted.Eagerly, WIFI_NETWORK_DEFAULT) override val secondaryNetworks: StateFlow<List<WifiNetworkModel>> = @@ -348,7 +488,7 @@ constructor( TAG, LogLevel.DEBUG, { str1 = prettyPrintActivity(activity) }, - { "onActivityChanged: $str1" } + { "onActivityChanged: $str1" }, ) } @@ -379,13 +519,15 @@ constructor( /** The currently primary wifi network. */ val primaryNetwork: WifiNetworkModel, /** The current secondary network(s), if any. Specifically excludes the primary network. */ - val secondaryNetworks: List<WifiNetworkModel> + val secondaryNetworks: List<WifiNetworkModel>, ) @SysUISingleton class Factory @Inject constructor( + @Application private val applicationContext: Context, + private val userRepository: UserRepository, @Application private val scope: CoroutineScope, @Main private val mainExecutor: Executor, @Background private val bgDispatcher: CoroutineDispatcher, @@ -395,6 +537,8 @@ constructor( ) { fun create(wifiManager: WifiManager): WifiRepositoryImpl { return WifiRepositoryImpl( + applicationContext, + userRepository, scope, mainExecutor, bgDispatcher, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SplitClockView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SplitClockView.java deleted file mode 100644 index 0d36b48e9099..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SplitClockView.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * 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 - */ - -package com.android.systemui.statusbar.policy; - -import android.app.ActivityManager; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.os.UserHandle; -import android.text.format.DateFormat; -import android.util.AttributeSet; -import android.widget.LinearLayout; -import android.widget.TextClock; - -import com.android.systemui.res.R; - -/** - * Container for a clock which has two separate views for the clock itself and AM/PM indicator. This - * is used to scale the clock independently of AM/PM. - */ -public class SplitClockView extends LinearLayout { - - private TextClock mTimeView; - private TextClock mAmPmView; - - private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - final String action = intent.getAction(); - if (Intent.ACTION_TIME_CHANGED.equals(action) - || Intent.ACTION_TIMEZONE_CHANGED.equals(action) - || Intent.ACTION_LOCALE_CHANGED.equals(action) - || Intent.ACTION_CONFIGURATION_CHANGED.equals(action) - || Intent.ACTION_USER_SWITCHED.equals(action)) { - updatePatterns(); - } - } - }; - - public SplitClockView(Context context, AttributeSet attrs) { - super(context, attrs); - } - - @Override - protected void onFinishInflate() { - super.onFinishInflate(); - mTimeView = findViewById(R.id.time_view); - mAmPmView = findViewById(R.id.am_pm_view); - mTimeView.setShowCurrentUserTime(true); - mAmPmView.setShowCurrentUserTime(true); - } - - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - - IntentFilter filter = new IntentFilter(); - filter.addAction(Intent.ACTION_TIME_CHANGED); - filter.addAction(Intent.ACTION_TIMEZONE_CHANGED); - filter.addAction(Intent.ACTION_LOCALE_CHANGED); - filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED); - filter.addAction(Intent.ACTION_USER_SWITCHED); - getContext().registerReceiverAsUser(mIntentReceiver, UserHandle.ALL, filter, null, null); - - updatePatterns(); - } - - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - getContext().unregisterReceiver(mIntentReceiver); - } - - private void updatePatterns() { - String formatString = DateFormat.getTimeFormatString(getContext(), - ActivityManager.getCurrentUser()); - int index = getAmPmPartEndIndex(formatString); - String timeString; - String amPmString; - if (index == -1) { - timeString = formatString; - amPmString = ""; - } else { - timeString = formatString.substring(0, index); - amPmString = formatString.substring(index); - } - mTimeView.setFormat12Hour(timeString); - mTimeView.setFormat24Hour(timeString); - mTimeView.setContentDescriptionFormat12Hour(formatString); - mTimeView.setContentDescriptionFormat24Hour(formatString); - mAmPmView.setFormat12Hour(amPmString); - mAmPmView.setFormat24Hour(amPmString); - } - - /** - * @return the index where the AM/PM part starts at the end in {@code formatString} including - * leading white spaces or {@code -1} if no AM/PM part is found or {@code formatString} - * doesn't end with AM/PM part - */ - private static int getAmPmPartEndIndex(String formatString) { - boolean hasAmPm = false; - int length = formatString.length(); - for (int i = length - 1; i >= 0; i--) { - char c = formatString.charAt(i); - boolean isAmPm = c == 'a'; - boolean isWhitespace = Character.isWhitespace(c); - if (isAmPm) { - hasAmPm = true; - } - if (isAmPm || isWhitespace) { - continue; - } - if (i == length - 1) { - - // First character was not AM/PM and not whitespace, so it's not ending with AM/PM. - return -1; - } else { - - // If we have AM/PM at all, return last index, or -1 to indicate that it's not - // ending with AM/PM. - return hasAmPm ? i + 1 : -1; - } - } - - // Only AM/PM and whitespaces? The whole string is AM/PM. Else: Only whitespaces in the - // string. - return hasAmPm ? 0 : -1; - } - -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java index c7bd5a1bb9a8..9187e3c43380 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java @@ -206,12 +206,14 @@ public interface StatusBarPolicyModule { @SysUISingleton @Provides static AccessPointControllerImpl provideAccessPointControllerImpl( + @Application Context context, UserManager userManager, UserTracker userTracker, @Main Executor mainExecutor, WifiPickerTrackerFactory wifiPickerTrackerFactory ) { AccessPointControllerImpl controller = new AccessPointControllerImpl( + context, userManager, userTracker, mainExecutor, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowView.java index 3f6ef16e2e5e..36d64a9b405e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowView.java @@ -35,11 +35,11 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.systemui.compose.ComposeInitializer; -import com.android.systemui.statusbar.core.StatusBarSimpleFragment; +import com.android.systemui.statusbar.core.StatusBarRootModernization; import com.android.systemui.statusbar.data.repository.StatusBarConfigurationController; /** - * Status bar view. + * Status bar view * We now extend WindowRootView so that we can host Compose views */ public class StatusBarWindowView extends FrameLayout { @@ -64,7 +64,7 @@ public class StatusBarWindowView extends FrameLayout { public void onAttachedToWindow() { super.onAttachedToWindow(); - if (StatusBarSimpleFragment.isEnabled()) { + if (StatusBarRootModernization.isEnabled()) { ComposeInitializer.INSTANCE.onAttachedToWindow(this); } } @@ -73,7 +73,7 @@ public class StatusBarWindowView extends FrameLayout { public void onDetachedFromWindow() { super.onDetachedFromWindow(); - if (StatusBarSimpleFragment.isEnabled()) { + if (StatusBarRootModernization.isEnabled()) { ComposeInitializer.INSTANCE.onDetachedFromWindow(this); } } diff --git a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt index f20ce63467f7..ad97b21ea60b 100644 --- a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt @@ -23,11 +23,13 @@ import android.app.admin.DevicePolicyManager import android.content.Context import android.content.IntentFilter import android.content.pm.UserInfo +import android.content.res.Resources import android.os.UserHandle import android.os.UserManager import android.provider.Settings import androidx.annotation.VisibleForTesting import com.android.app.tracing.coroutines.launchTraced as launch +import com.android.internal.statusbar.IStatusBarService import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow @@ -109,6 +111,9 @@ interface UserRepository { /** Whether logout for secondary users is enabled by admin device policy. */ val isSecondaryUserLogoutEnabled: StateFlow<Boolean> + /** Whether logout into system user is enabled. */ + val isLogoutToSystemUserEnabled: StateFlow<Boolean> + /** Asynchronously refresh the list of users. This will cause [userInfos] to be updated. */ fun refreshUsers() @@ -121,6 +126,9 @@ interface UserRepository { /** Performs logout logout for secondary users. */ suspend fun logOutSecondaryUser() + /** Performs logout into the system user. */ + suspend fun logOutToSystemUser() + /** * Returns the user ID of the "main user" of the device. This user may have access to certain * features which are limited to at most one user. There will never be more than one main user @@ -143,6 +151,7 @@ class UserRepositoryImpl @Inject constructor( @Application private val appContext: Context, + @Main private val resources: Resources, private val manager: UserManager, @Application private val applicationScope: CoroutineScope, @Main private val mainDispatcher: CoroutineDispatcher, @@ -151,6 +160,7 @@ constructor( private val tracker: UserTracker, private val devicePolicyManager: DevicePolicyManager, private val broadcastDispatcher: BroadcastDispatcher, + private val statusBarService: IStatusBarService, ) : UserRepository { private val _userSwitcherSettings: StateFlow<UserSwitcherSettingsModel> = @@ -275,12 +285,34 @@ constructor( .stateIn(applicationScope, SharingStarted.Eagerly, false) @SuppressLint("MissingPermission") + override val isLogoutToSystemUserEnabled: StateFlow<Boolean> = + selectedUser + .flatMapLatestConflated { selectedUser -> + if (selectedUser.isEligibleForLogout()) { + flowOf( + resources.getBoolean(R.bool.config_userSwitchingMustGoThroughLoginScreen) + ) + } else { + flowOf(false) + } + } + .stateIn(applicationScope, SharingStarted.Eagerly, false) + + @SuppressLint("MissingPermission") override suspend fun logOutSecondaryUser() { if (isSecondaryUserLogoutEnabled.value) { withContext(backgroundDispatcher) { devicePolicyManager.logoutUser() } } } + override suspend fun logOutToSystemUser() { + // TODO(b/377493351) : start using proper logout API once it is available. + // Using reboot is a temporary solution. + if (isLogoutToSystemUserEnabled.value) { + withContext(backgroundDispatcher) { statusBarService.reboot(false) } + } + } + @SuppressLint("MissingPermission") override fun refreshUsers() { applicationScope.launch { @@ -316,42 +348,53 @@ constructor( private suspend fun getSettings(): UserSwitcherSettingsModel { return withContext(backgroundDispatcher) { - val isSimpleUserSwitcher = - globalSettings.getInt( - SETTING_SIMPLE_USER_SWITCHER, - if ( - appContext.resources.getBoolean( - com.android.internal.R.bool.config_expandLockScreenUserSwitcher - ) - ) { - 1 - } else { - 0 - }, - ) != 0 - - val isAddUsersFromLockscreen = - globalSettings.getInt(Settings.Global.ADD_USERS_WHEN_LOCKED, 0) != 0 - - val isUserSwitcherEnabled = - globalSettings.getInt( - Settings.Global.USER_SWITCHER_ENABLED, - if ( - appContext.resources.getBoolean( - com.android.internal.R.bool.config_showUserSwitcherByDefault - ) - ) { - 1 - } else { - 0 - }, - ) != 0 - - UserSwitcherSettingsModel( - isSimpleUserSwitcher = isSimpleUserSwitcher, - isAddUsersFromLockscreen = isAddUsersFromLockscreen, - isUserSwitcherEnabled = isUserSwitcherEnabled, - ) + if ( + // TODO(b/378068979): remove once login screen-specific logic + // is implemented at framework level. + appContext.resources.getBoolean(R.bool.config_userSwitchingMustGoThroughLoginScreen) + ) { + UserSwitcherSettingsModel( + isSimpleUserSwitcher = false, + isAddUsersFromLockscreen = false, + isUserSwitcherEnabled = false, + ) + } else { + val isSimpleUserSwitcher = + globalSettings.getInt( + SETTING_SIMPLE_USER_SWITCHER, + if ( + appContext.resources.getBoolean( + com.android.internal.R.bool.config_expandLockScreenUserSwitcher + ) + ) { + 1 + } else { + 0 + }, + ) != 0 + + val isAddUsersFromLockscreen = + globalSettings.getInt(Settings.Global.ADD_USERS_WHEN_LOCKED, 0) != 0 + + val isUserSwitcherEnabled = + globalSettings.getInt( + Settings.Global.USER_SWITCHER_ENABLED, + if ( + appContext.resources.getBoolean( + com.android.internal.R.bool.config_showUserSwitcherByDefault + ) + ) { + 1 + } else { + 0 + }, + ) != 0 + UserSwitcherSettingsModel( + isSimpleUserSwitcher = isSimpleUserSwitcher, + isAddUsersFromLockscreen = isAddUsersFromLockscreen, + isUserSwitcherEnabled = isUserSwitcherEnabled, + ) + } } } diff --git a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserSwitcherRepository.kt b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserSwitcherRepository.kt index a7983605eac9..bcbd679b35eb 100644 --- a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserSwitcherRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserSwitcherRepository.kt @@ -21,6 +21,7 @@ import android.graphics.drawable.Drawable import android.os.Handler import android.os.UserManager import android.provider.Settings.Global.USER_SWITCHER_ENABLED +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow import com.android.systemui.dagger.SysUISingleton @@ -40,7 +41,6 @@ import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf -import com.android.app.tracing.coroutines.launchTraced as launch import kotlinx.coroutines.withContext interface UserSwitcherRepository { @@ -67,6 +67,9 @@ constructor( private val showUserSwitcherForSingleUser = context.resources.getBoolean(R.bool.qs_show_user_switcher_for_single_user) + private val userSwitchingMustGoThroughLoginScreen = + context.resources.getBoolean(R.bool.config_userSwitchingMustGoThroughLoginScreen) + override val isEnabled: Flow<Boolean> = conflatedCallbackFlow { suspend fun updateState() { trySendWithFailureLogging(isUserSwitcherEnabled(), TAG) @@ -135,7 +138,13 @@ constructor( private suspend fun isUserSwitcherEnabled(): Boolean { return withContext(bgDispatcher) { - userManager.isUserSwitcherEnabled(showUserSwitcherForSingleUser) + // TODO(b/378068979): remove once login screen-specific logic + // is implemented at framework level. + if (userSwitchingMustGoThroughLoginScreen) { + false + } else { + userManager.isUserSwitcherEnabled(showUserSwitcherForSingleUser) + } } } diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserLogoutInteractor.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserLogoutInteractor.kt index 154f1dc3e747..f2dd25fecf08 100644 --- a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserLogoutInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserLogoutInteractor.kt @@ -23,7 +23,10 @@ import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.user.data.repository.UserRepository import javax.inject.Inject import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.stateIn /** Encapsulates business logic to for the logout. */ @SysUISingleton @@ -33,11 +36,22 @@ constructor( private val userRepository: UserRepository, @Application private val applicationScope: CoroutineScope, ) { - val isLogoutEnabled: StateFlow<Boolean> = userRepository.isSecondaryUserLogoutEnabled + + val isLogoutEnabled: StateFlow<Boolean> = + combine( + userRepository.isSecondaryUserLogoutEnabled, + userRepository.isLogoutToSystemUserEnabled, + Boolean::or, + ) + .stateIn(applicationScope, SharingStarted.Eagerly, false) fun logOut() { - if (userRepository.isSecondaryUserLogoutEnabled.value) { - applicationScope.launch { userRepository.logOutSecondaryUser() } + applicationScope.launch { + if (userRepository.isSecondaryUserLogoutEnabled.value) { + userRepository.logOutSecondaryUser() + } else if (userRepository.isLogoutToSystemUserEnabled.value) { + userRepository.logOutToSystemUser() + } } } } diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialog.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialog.kt index 4fc9a7c9ae01..5c0cc8150d70 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialog.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialog.kt @@ -18,9 +18,13 @@ package com.android.systemui.volume.dialog import android.app.Dialog import android.content.Context +import android.graphics.PixelFormat import android.os.Bundle import android.view.MotionEvent +import android.view.ViewGroup +import android.view.WindowManager import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.res.R import com.android.systemui.volume.Events import com.android.systemui.volume.dialog.domain.interactor.VolumeDialogVisibilityInteractor import com.android.systemui.volume.dialog.ui.binder.VolumeDialogViewBinder @@ -32,10 +36,34 @@ constructor( @Application context: Context, private val viewBinder: VolumeDialogViewBinder, private val visibilityInteractor: VolumeDialogVisibilityInteractor, -) : Dialog(context) { +) : Dialog(context, R.style.Theme_SystemUI_Dialog_Volume) { + + init { + with(window!!) { + addFlags( + WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or + WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or + WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH or + WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED + ) + addPrivateFlags(WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY) + + setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY) + setWindowAnimations(-1) + setFormat(PixelFormat.TRANSLUCENT) + + attributes = + attributes.apply { + title = "VolumeDialog" // Not the same as Window#setTitle + } + setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT) + } + setCanceledOnTouchOutside(true) + } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + setContentView(R.layout.volume_dialog) viewBinder.bind(this) } diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/dagger/VolumeDialogComponent.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/dagger/VolumeDialogComponent.kt index 9440a9364b62..fb157958a630 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dialog/dagger/VolumeDialogComponent.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/dagger/VolumeDialogComponent.kt @@ -16,6 +16,7 @@ package com.android.systemui.volume.dialog.dagger +import com.android.systemui.volume.dialog.dagger.module.VolumeDialogModule import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope import com.android.systemui.volume.dialog.sliders.dagger.VolumeDialogSliderComponent @@ -28,7 +29,7 @@ import kotlinx.coroutines.CoroutineScope * [com.android.systemui.volume.dialog.VolumeDialogPlugin] and lives alongside it. */ @VolumeDialogScope -@Subcomponent(modules = []) +@Subcomponent(modules = [VolumeDialogModule::class]) interface VolumeDialogComponent { /** diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/dagger/module/VolumeDialogModule.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/dagger/module/VolumeDialogModule.kt new file mode 100644 index 000000000000..025e269b70b6 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/dagger/module/VolumeDialogModule.kt @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.volume.dialog.dagger.module + +import com.android.systemui.volume.dialog.ringer.data.repository.VolumeDialogRingerFeedbackRepository +import com.android.systemui.volume.dialog.ringer.data.repository.VolumeDialogRingerFeedbackRepositoryImpl +import dagger.Binds +import dagger.Module + +/** Dagger module for volume dialog code in the volume package */ +@Module +interface VolumeDialogModule { + + @Binds + fun bindVolumeDialogRingerFeedbackRepository( + ringerFeedbackRepository: VolumeDialogRingerFeedbackRepositoryImpl + ): VolumeDialogRingerFeedbackRepository +} diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/data/repository/VolumeDialogRingerFeedbackRepository.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/data/repository/VolumeDialogRingerFeedbackRepository.kt new file mode 100644 index 000000000000..263972b937d4 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/data/repository/VolumeDialogRingerFeedbackRepository.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.volume.dialog.ringer.data.repository + +import android.content.Context +import com.android.systemui.Prefs +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.dagger.qualifiers.Background +import javax.inject.Inject +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.withContext + +interface VolumeDialogRingerFeedbackRepository { + + /** gets number of shown toasts */ + suspend fun getToastCount(): Int + + /** updates number of shown toasts */ + suspend fun updateToastCount(toastCount: Int) +} + +class VolumeDialogRingerFeedbackRepositoryImpl +@Inject +constructor( + @Application private val applicationContext: Context, + @Background val backgroundDispatcher: CoroutineDispatcher, +) : VolumeDialogRingerFeedbackRepository { + + override suspend fun getToastCount(): Int = + withContext(backgroundDispatcher) { + return@withContext Prefs.getInt( + applicationContext, + Prefs.Key.SEEN_RINGER_GUIDANCE_COUNT, + 0, + ) + } + + override suspend fun updateToastCount(toastCount: Int) { + withContext(backgroundDispatcher) { + Prefs.putInt(applicationContext, Prefs.Key.SEEN_RINGER_GUIDANCE_COUNT, toastCount + 1) + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/domain/VolumeDialogRingerInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/domain/VolumeDialogRingerInteractor.kt index 281e57fec855..b83613ba4f8c 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/domain/VolumeDialogRingerInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/domain/VolumeDialogRingerInteractor.kt @@ -26,6 +26,7 @@ import com.android.settingslib.volume.shared.model.RingerMode import com.android.systemui.plugins.VolumeDialogController import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog import com.android.systemui.volume.dialog.domain.interactor.VolumeDialogStateInteractor +import com.android.systemui.volume.dialog.ringer.data.repository.VolumeDialogRingerFeedbackRepository import com.android.systemui.volume.dialog.ringer.shared.model.VolumeDialogRingerModel import com.android.systemui.volume.dialog.shared.model.VolumeDialogStateModel import javax.inject.Inject @@ -45,6 +46,7 @@ constructor( volumeDialogStateInteractor: VolumeDialogStateInteractor, private val controller: VolumeDialogController, private val audioSystemRepository: AudioSystemRepository, + private val ringerFeedbackRepository: VolumeDialogRingerFeedbackRepository, ) { val ringerModel: Flow<VolumeDialogRingerModel> = @@ -84,4 +86,12 @@ constructor( fun scheduleTouchFeedback() { controller.scheduleTouchFeedback() } + + suspend fun getToastCount(): Int { + return ringerFeedbackRepository.getToastCount() + } + + suspend fun updateToastCount(toastCount: Int) { + ringerFeedbackRepository.updateToastCount(toastCount) + } } diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModel.kt index d4da226152f3..e040638324ac 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModel.kt @@ -16,17 +16,23 @@ package com.android.systemui.volume.dialog.ringer.ui.viewmodel +import android.content.Context import android.media.AudioAttributes import android.media.AudioManager.RINGER_MODE_NORMAL import android.media.AudioManager.RINGER_MODE_SILENT import android.media.AudioManager.RINGER_MODE_VIBRATE import android.os.VibrationEffect +import android.widget.Toast +import com.android.internal.R as internalR +import com.android.settingslib.Utils import com.android.settingslib.volume.shared.model.RingerMode +import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.res.R import com.android.systemui.statusbar.VibratorHelper import com.android.systemui.volume.Events import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog +import com.android.systemui.volume.dialog.domain.interactor.VolumeDialogVisibilityInteractor import com.android.systemui.volume.dialog.ringer.domain.VolumeDialogRingerInteractor import com.android.systemui.volume.dialog.ringer.shared.model.VolumeDialogRingerModel import com.android.systemui.volume.dialog.shared.VolumeDialogLogger @@ -40,26 +46,37 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.launch + +private const val SHOW_RINGER_TOAST_COUNT = 12 class VolumeDialogRingerDrawerViewModel @AssistedInject constructor( + @Application private val applicationContext: Context, @VolumeDialog private val coroutineScope: CoroutineScope, @Background private val backgroundDispatcher: CoroutineDispatcher, private val interactor: VolumeDialogRingerInteractor, private val vibrator: VibratorHelper, private val volumeDialogLogger: VolumeDialogLogger, + private val visibilityInteractor: VolumeDialogVisibilityInteractor, ) { private val drawerState = MutableStateFlow<RingerDrawerState>(RingerDrawerState.Initial) val ringerViewModel: StateFlow<RingerViewModelState> = combine(interactor.ringerModel, drawerState) { ringerModel, state -> + level = ringerModel.level + levelMax = ringerModel.levelMax ringerModel.toViewModel(state) } .flowOn(backgroundDispatcher) .stateIn(coroutineScope, SharingStarted.Eagerly, RingerViewModelState.Unavailable) + // Level and Maximum level of Ring Stream. + private var level = -1 + private var levelMax = -1 + // Vibration attributes. private val sonificiationVibrationAttributes = AudioAttributes.Builder() @@ -71,8 +88,10 @@ constructor( if (drawerState.value is RingerDrawerState.Open) { Events.writeEvent(Events.EVENT_RINGER_TOGGLE, ringerMode.value) provideTouchFeedback(ringerMode) + maybeShowToast(ringerMode) interactor.setRingerMode(ringerMode) } + visibilityInteractor.resetDismissTimeout() drawerState.value = when (drawerState.value) { is RingerDrawerState.Initial -> { @@ -201,6 +220,46 @@ constructor( } } + private fun maybeShowToast(ringerMode: RingerMode) { + coroutineScope.launch { + val seenToastCount = interactor.getToastCount() + if (seenToastCount > SHOW_RINGER_TOAST_COUNT) { + return@launch + } + + val toastText = + when (ringerMode.value) { + RINGER_MODE_NORMAL -> { + if (level != -1 && levelMax != -1) { + applicationContext.getString( + R.string.volume_dialog_ringer_guidance_ring, + Utils.formatPercentage(level.toLong(), levelMax.toLong()), + ) + } else { + null + } + } + + RINGER_MODE_SILENT -> + applicationContext.getString( + internalR.string.volume_dialog_ringer_guidance_silent + ) + + RINGER_MODE_VIBRATE -> + applicationContext.getString( + internalR.string.volume_dialog_ringer_guidance_vibrate + ) + + else -> + applicationContext.getString( + internalR.string.volume_dialog_ringer_guidance_vibrate + ) + } + toastText?.let { Toast.makeText(applicationContext, it, Toast.LENGTH_SHORT).show() } + interactor.updateToastCount(seenToastCount) + } + } + @AssistedFactory interface Factory { fun create(): VolumeDialogRingerDrawerViewModel diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt index 78eabb27abab..d9a945cfedfd 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt @@ -17,21 +17,22 @@ package com.android.systemui.volume.dialog.ui.binder import android.app.Dialog -import android.graphics.Color -import android.graphics.PixelFormat -import android.graphics.drawable.ColorDrawable +import android.graphics.Rect +import android.graphics.Region import android.view.Gravity import android.view.View import android.view.ViewGroup -import android.view.Window -import android.view.WindowManager +import android.view.ViewTreeObserver +import android.view.ViewTreeObserver.InternalInsetsInfo +import android.widget.FrameLayout +import androidx.annotation.GravityInt import com.android.internal.view.RotationPolicy import com.android.systemui.lifecycle.WindowLifecycleState import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.lifecycle.viewModel import com.android.systemui.res.R +import com.android.systemui.util.children import com.android.systemui.volume.SystemUIInterpolators -import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope import com.android.systemui.volume.dialog.ringer.ui.binder.VolumeDialogRingerViewBinder import com.android.systemui.volume.dialog.settings.ui.binder.VolumeDialogSettingsButtonViewBinder @@ -52,6 +53,8 @@ import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch +import kotlinx.coroutines.suspendCancellableCoroutine /** Binds the root view of the Volume Dialog. */ @OptIn(ExperimentalCoroutinesApi::class) @@ -61,65 +64,41 @@ class VolumeDialogViewBinder constructor( private val volumeResources: VolumeDialogResources, private val gravityViewModel: VolumeDialogGravityViewModel, - private val viewModelFactory: VolumeDialogViewModel.Factory, + private val dialogViewModelFactory: VolumeDialogViewModel.Factory, private val jankListenerFactory: JankListenerFactory, private val tracer: VolumeTracer, - @VolumeDialog private val coroutineScope: CoroutineScope, private val volumeDialogRingerViewBinder: VolumeDialogRingerViewBinder, private val slidersViewBinder: VolumeDialogSlidersViewBinder, private val settingsButtonViewBinder: VolumeDialogSettingsButtonViewBinder, ) { fun bind(dialog: Dialog) { - setupDialog(dialog) - val view: View = dialog.requireViewById(R.id.volume_dialog_container) - view.alpha = 0f - view.repeatWhenAttached { - view.viewModel( + // Root view of the Volume Dialog. + val root: ViewGroup = dialog.requireViewById(R.id.volume_dialog_root) + // Volume Dialog container view that contains the dialog itself without the floating sliders + val container: View = root.requireViewById(R.id.volume_dialog_container) + container.alpha = 0f + container.repeatWhenAttached { + root.viewModel( traceName = "VolumeDialogViewBinder", minWindowLifecycleState = WindowLifecycleState.ATTACHED, - factory = { viewModelFactory.create() }, + factory = { dialogViewModelFactory.create() }, ) { viewModel -> + animateVisibility(container, dialog, viewModel.dialogVisibilityModel) + viewModel.dialogTitle.onEach { dialog.window?.setTitle(it) }.launchIn(this) + gravityViewModel.dialogGravity + .onEach { container.setLayoutGravity(it) } + .launchIn(this) - animateVisibility(view, dialog, viewModel.dialogVisibilityModel) + launch { root.viewTreeObserver.computeInternalInsetsListener(root) } awaitCancellation() } } - volumeDialogRingerViewBinder.bind(view) - slidersViewBinder.bind(view) - settingsButtonViewBinder.bind(view) - } - - /** Configures [Window] for the [Dialog]. */ - private fun setupDialog(dialog: Dialog) { - with(dialog.window!!) { - clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND) - addFlags( - WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or - WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or - WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH or - WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED - ) - addPrivateFlags(WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY) - - requestFeature(Window.FEATURE_NO_TITLE) - setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) - setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY) - setWindowAnimations(-1) - setFormat(PixelFormat.TRANSLUCENT) - - attributes = - attributes.apply { - title = "VolumeDialog" // Not the same as Window#setTitle - } - setLayout(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT) - - gravityViewModel.dialogGravity.onEach { setGravity(it) }.launchIn(coroutineScope) - } - dialog.setContentView(R.layout.volume_dialog) - dialog.setCanceledOnTouchOutside(true) + volumeDialogRingerViewBinder.bind(root) + slidersViewBinder.bind(root) + settingsButtonViewBinder.bind(root) } private fun CoroutineScope.animateVisibility( @@ -209,4 +188,33 @@ constructor( } animator.suspendAnimate(jankListenerFactory.dismiss(this, duration)) } + + private suspend fun ViewTreeObserver.computeInternalInsetsListener(viewGroup: ViewGroup) = + suspendCancellableCoroutine<Unit> { continuation -> + val listener = + ViewTreeObserver.OnComputeInternalInsetsListener { inoutInfo -> + viewGroup.fillTouchableBounds(inoutInfo) + } + addOnComputeInternalInsetsListener(listener) + continuation.invokeOnCancellation { removeOnComputeInternalInsetsListener(listener) } + } + + private fun ViewGroup.fillTouchableBounds(internalInsetsInfo: InternalInsetsInfo) { + for (child in children) { + val boundsRect = Rect() + internalInsetsInfo.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_REGION) + + child.getBoundsInWindow(boundsRect, false) + internalInsetsInfo.touchableRegion.op(boundsRect, Region.Op.UNION) + } + val boundsRect = Rect() + getBoundsInWindow(boundsRect, false) + } + + private fun View.setLayoutGravity(@GravityInt newGravity: Int) { + val frameLayoutParams = + layoutParams as? FrameLayout.LayoutParams + ?: error("View must be a child of a FrameLayout") + layoutParams = frameLayoutParams.apply { gravity = newGravity } + } } diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java index 53e6b4f82b7e..761993b3cda7 100644 --- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java +++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java @@ -330,13 +330,19 @@ public class WalletScreenController implements QAWalletCardViewInfo(Context context, WalletCard walletCard) { mWalletCard = walletCard; Icon cardImageIcon = mWalletCard.getCardImage(); - if (cardImageIcon.getType() == Icon.TYPE_URI) { - mCardDrawable = null; - } else { + if (cardImageIcon.getType() == Icon.TYPE_BITMAP + || cardImageIcon.getType() == Icon.TYPE_ADAPTIVE_BITMAP) { mCardDrawable = mWalletCard.getCardImage().loadDrawable(context); + } else { + mCardDrawable = null; } Icon icon = mWalletCard.getCardIcon(); - mIconDrawable = icon == null ? null : icon.loadDrawable(context); + if (icon != null && (icon.getType() == Icon.TYPE_BITMAP + || icon.getType() == Icon.TYPE_ADAPTIVE_BITMAP)) { + mIconDrawable = icon.loadDrawable(context); + } else { + mIconDrawable = null; + } } @Override diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java index 1a399341f12c..ca9b866e2d18 100644 --- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java +++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java @@ -283,6 +283,11 @@ public class WalletView extends FrameLayout implements WalletCardCarousel.OnCard return mCardLabel; } + @VisibleForTesting + ImageView getIcon() { + return mIcon; + } + @Nullable private static Drawable getHeaderIcon(Context context, WalletCardViewInfo walletCard) { Drawable icon = walletCard.getIcon(); diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java index 8039e00159f0..073781e6101d 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java @@ -271,6 +271,12 @@ public final class WMShell implements // No op. } }, mSysUiMainExecutor); + pip.addOnIsInPipStateChangedListener((isInPip) -> { + if (!isInPip) { + mSysUiState.setFlag(SYSUI_STATE_DISABLE_GESTURE_PIP_ANIMATING, false) + .commitUpdate(mDisplayTracker.getDefaultDisplayId()); + } + }); mSysUiState.addCallback(sysUiStateFlag -> { mIsSysUiStateValid = (sysUiStateFlag & INVALID_SYSUI_STATE_MASK) == 0; pip.onSystemUiStateChanged(mIsSysUiStateValid, sysUiStateFlag); diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt index 2b167e4c5da4..65b62737b692 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt @@ -56,6 +56,7 @@ import com.android.systemui.plugins.clocks.ThemeConfig import com.android.systemui.plugins.clocks.ZenData import com.android.systemui.plugins.clocks.ZenData.ZenMode import com.android.systemui.res.R +import com.android.systemui.settings.UserTracker import com.android.systemui.statusbar.policy.BatteryController import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.statusbar.policy.ZenModeController @@ -128,6 +129,7 @@ class ClockEventControllerTest : SysuiTestCase() { @Mock private lateinit var largeClockEvents: ClockFaceEvents @Mock private lateinit var parentView: View @Mock private lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor + @Mock private lateinit var userTracker: UserTracker @Mock private lateinit var zenModeController: ZenModeController private var zenModeControllerCallback: ZenModeController.Callback? = null @@ -153,6 +155,7 @@ class ClockEventControllerTest : SysuiTestCase() { .thenReturn(ClockFaceConfig(tickRate = ClockTickRate.PER_MINUTE)) whenever(smallClockController.theme).thenReturn(ThemeConfig(true, null)) whenever(largeClockController.theme).thenReturn(ThemeConfig(true, null)) + whenever(userTracker.userId).thenReturn(1) zenModeRepository.addMode(MANUAL_DND_INACTIVE) @@ -177,6 +180,7 @@ class ClockEventControllerTest : SysuiTestCase() { withDeps.featureFlags, zenModeController, kosmos.zenModeInteractor, + userTracker, ) underTest.clock = clock diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java index 96f4a60271d2..b4c69529741e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java @@ -46,7 +46,6 @@ import androidx.test.filters.SmallTest; import com.android.internal.logging.InstanceId; import com.android.internal.logging.UiEventLogger; -import com.android.systemui.Flags; import com.android.systemui.SysuiTestCase; import com.android.systemui.biometrics.AuthController; import com.android.systemui.broadcast.BroadcastDispatcher; @@ -222,7 +221,6 @@ public class DozeTriggersTest extends SysuiTestCase { } @Test - @EnableFlags(Flags.FLAG_NOTIFICATION_PULSING_FIX) public void testOnNotification_alreadyPulsing_notificationNotSuppressed() { // GIVEN device is pulsing Runnable pulseSuppressListener = mock(Runnable.class); diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt index fb376ce3ca40..3ddd4b58211d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt @@ -289,6 +289,7 @@ class LegacyMediaDataManagerImplTest(flags: FlagsParameterization) : SysuiTestCa } verify(smartspaceManager).createSmartspaceSession(capture(smartSpaceConfigBuilderCaptor)) mediaControllerFactory.setControllerForToken(session.sessionToken, controller) + whenever(controller.sessionToken).thenReturn(session.sessionToken) whenever(controller.transportControls).thenReturn(transportControls) whenever(controller.playbackInfo).thenReturn(playbackInfo) whenever(controller.metadata).thenReturn(metadataBuilder.build()) @@ -1599,6 +1600,7 @@ class LegacyMediaDataManagerImplTest(flags: FlagsParameterization) : SysuiTestCa verify(logger, never()).logResumeMediaAdded(anyInt(), eq(PACKAGE_NAME), any()) } + @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_BUTTON_MEDIA3) @Test fun testTooManyCompactActions_isTruncated() { // GIVEN a notification where too many compact actions were specified @@ -1635,6 +1637,7 @@ class LegacyMediaDataManagerImplTest(flags: FlagsParameterization) : SysuiTestCa .isEqualTo(LegacyMediaDataManagerImpl.MAX_COMPACT_ACTIONS) } + @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_BUTTON_MEDIA3) @Test fun testTooManyNotificationActions_isTruncated() { // GIVEN a notification where too many notification actions are added @@ -1670,6 +1673,7 @@ class LegacyMediaDataManagerImplTest(flags: FlagsParameterization) : SysuiTestCa .isEqualTo(LegacyMediaDataManagerImpl.MAX_NOTIFICATION_ACTIONS) } + @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_BUTTON_MEDIA3) @Test fun testPlaybackActions_noState_usesNotification() { val desc = "Notification Action" @@ -1703,6 +1707,7 @@ class LegacyMediaDataManagerImplTest(flags: FlagsParameterization) : SysuiTestCa assertThat(mediaDataCaptor.value!!.actions[0]!!.contentDescription).isEqualTo(desc) } + @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_BUTTON_MEDIA3) @Test fun testPlaybackActions_hasPrevNext() { val customDesc = arrayOf("custom 1", "custom 2", "custom 3", "custom 4") @@ -1746,6 +1751,7 @@ class LegacyMediaDataManagerImplTest(flags: FlagsParameterization) : SysuiTestCa assertThat(actions.custom1!!.contentDescription).isEqualTo(customDesc[1]) } + @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_BUTTON_MEDIA3) @Test fun testPlaybackActions_noPrevNext_usesCustom() { val customDesc = arrayOf("custom 1", "custom 2", "custom 3", "custom 4", "custom 5") @@ -1778,6 +1784,7 @@ class LegacyMediaDataManagerImplTest(flags: FlagsParameterization) : SysuiTestCa assertThat(actions.custom1!!.contentDescription).isEqualTo(customDesc[3]) } + @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_BUTTON_MEDIA3) @Test fun testPlaybackActions_connecting() { val stateActions = PlaybackState.ACTION_PLAY @@ -1797,6 +1804,7 @@ class LegacyMediaDataManagerImplTest(flags: FlagsParameterization) : SysuiTestCa .isEqualTo(context.getString(R.string.controls_media_button_connecting)) } + @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_BUTTON_MEDIA3) @Test fun testPlaybackActions_reservedSpace() { val customDesc = arrayOf("custom 1", "custom 2", "custom 3", "custom 4") @@ -1835,6 +1843,7 @@ class LegacyMediaDataManagerImplTest(flags: FlagsParameterization) : SysuiTestCa assertThat(actions.reservePrev).isTrue() } + @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_BUTTON_MEDIA3) @Test fun testPlaybackActions_playPause_hasButton() { val stateActions = PlaybackState.ACTION_PLAY_PAUSE @@ -1998,6 +2007,7 @@ class LegacyMediaDataManagerImplTest(flags: FlagsParameterization) : SysuiTestCa assertThat(mediaDataCaptor.value.semanticActions).isNotNull() } + @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_BUTTON_MEDIA3) @Test fun testPlaybackStateNull_Pause_keyExists_callsListener() { whenever(controller.playbackState).thenReturn(null) @@ -2056,6 +2066,7 @@ class LegacyMediaDataManagerImplTest(flags: FlagsParameterization) : SysuiTestCa assertThat(mediaDataCaptor.value.isClearable).isFalse() } + @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_BUTTON_MEDIA3) @Test fun testRetain_notifPlayer_notifRemoved_setToResume() { fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true) @@ -2086,6 +2097,7 @@ class LegacyMediaDataManagerImplTest(flags: FlagsParameterization) : SysuiTestCa ) } + @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_BUTTON_MEDIA3) @Test fun testRetain_notifPlayer_sessionDestroyed_doesNotChange() { fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true) @@ -2104,6 +2116,7 @@ class LegacyMediaDataManagerImplTest(flags: FlagsParameterization) : SysuiTestCa .onMediaDataLoaded(eq(PACKAGE_NAME), any(), any(), anyBoolean(), anyInt(), anyBoolean()) } + @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_BUTTON_MEDIA3) @Test fun testRetain_notifPlayer_removeWhileActive_fullyRemoved() { fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true) diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt index 7d364bd832f2..e5483c0980c0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt @@ -103,7 +103,6 @@ import org.mockito.ArgumentMatchers.anyInt import org.mockito.Captor import org.mockito.Mock import org.mockito.Mockito -import org.mockito.Mockito.mock import org.mockito.Mockito.never import org.mockito.Mockito.reset import org.mockito.Mockito.verify @@ -113,6 +112,7 @@ import org.mockito.junit.MockitoJUnit import org.mockito.kotlin.any import org.mockito.kotlin.capture import org.mockito.kotlin.eq +import org.mockito.kotlin.mock import org.mockito.kotlin.whenever import org.mockito.quality.Strictness import platform.test.runner.parameterized.ParameterizedAndroidJunit4 @@ -312,6 +312,7 @@ class MediaDataProcessorTest(flags: FlagsParameterization) : SysuiTestCase() { } verify(smartspaceManager).createSmartspaceSession(capture(smartSpaceConfigBuilderCaptor)) mediaControllerFactory.setControllerForToken(session.sessionToken, controller) + whenever(controller.sessionToken).thenReturn(session.sessionToken) whenever(controller.transportControls).thenReturn(transportControls) whenever(controller.playbackInfo).thenReturn(playbackInfo) whenever(controller.metadata).thenReturn(metadataBuilder.build()) @@ -596,7 +597,7 @@ class MediaDataProcessorTest(flags: FlagsParameterization) : SysuiTestCase() { fun testOnNotificationAdded_emptyTitle_hasPlaceholder() { // When the manager has a notification with an empty title, and the app is not // required to include a non-empty title - val mockPackageManager = mock(PackageManager::class.java) + val mockPackageManager = mock<PackageManager>() context.setMockPackageManager(mockPackageManager) whenever(mockPackageManager.getApplicationLabel(any())).thenReturn(APP_NAME) whenever(controller.metadata) @@ -626,7 +627,7 @@ class MediaDataProcessorTest(flags: FlagsParameterization) : SysuiTestCase() { fun testOnNotificationAdded_blankTitle_hasPlaceholder() { // GIVEN that the manager has a notification with a blank title, and the app is not // required to include a non-empty title - val mockPackageManager = mock(PackageManager::class.java) + val mockPackageManager = mock<PackageManager>() context.setMockPackageManager(mockPackageManager) whenever(mockPackageManager.getApplicationLabel(any())).thenReturn(APP_NAME) whenever(controller.metadata) @@ -656,7 +657,7 @@ class MediaDataProcessorTest(flags: FlagsParameterization) : SysuiTestCase() { fun testOnNotificationAdded_emptyMetadata_usesNotificationTitle() { // When the app sets the metadata title fields to empty strings, but does include a // non-blank notification title - val mockPackageManager = mock(PackageManager::class.java) + val mockPackageManager = mock<PackageManager>() context.setMockPackageManager(mockPackageManager) whenever(mockPackageManager.getApplicationLabel(any())).thenReturn(APP_NAME) whenever(controller.metadata) @@ -1610,6 +1611,7 @@ class MediaDataProcessorTest(flags: FlagsParameterization) : SysuiTestCase() { verify(logger, never()).logResumeMediaAdded(anyInt(), eq(PACKAGE_NAME), any()) } + @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_BUTTON_MEDIA3) @Test fun testTooManyCompactActions_isTruncated() { // GIVEN a notification where too many compact actions were specified @@ -1646,6 +1648,7 @@ class MediaDataProcessorTest(flags: FlagsParameterization) : SysuiTestCase() { .isEqualTo(MediaDataProcessor.MAX_COMPACT_ACTIONS) } + @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_BUTTON_MEDIA3) @Test fun testTooManyNotificationActions_isTruncated() { // GIVEN a notification where too many notification actions are added @@ -1681,6 +1684,7 @@ class MediaDataProcessorTest(flags: FlagsParameterization) : SysuiTestCase() { .isEqualTo(MediaDataProcessor.MAX_NOTIFICATION_ACTIONS) } + @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_BUTTON_MEDIA3) @Test fun testPlaybackActions_noState_usesNotification() { val desc = "Notification Action" @@ -1714,6 +1718,7 @@ class MediaDataProcessorTest(flags: FlagsParameterization) : SysuiTestCase() { assertThat(mediaDataCaptor.value!!.actions[0]!!.contentDescription).isEqualTo(desc) } + @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_BUTTON_MEDIA3) @Test fun testPlaybackActions_hasPrevNext() { val customDesc = arrayOf("custom 1", "custom 2", "custom 3", "custom 4") @@ -1757,6 +1762,7 @@ class MediaDataProcessorTest(flags: FlagsParameterization) : SysuiTestCase() { assertThat(actions.custom1!!.contentDescription).isEqualTo(customDesc[1]) } + @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_BUTTON_MEDIA3) @Test fun testPlaybackActions_noPrevNext_usesCustom() { val customDesc = arrayOf("custom 1", "custom 2", "custom 3", "custom 4", "custom 5") @@ -1789,6 +1795,7 @@ class MediaDataProcessorTest(flags: FlagsParameterization) : SysuiTestCase() { assertThat(actions.custom1!!.contentDescription).isEqualTo(customDesc[3]) } + @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_BUTTON_MEDIA3) @Test fun testPlaybackActions_connecting() { val stateActions = PlaybackState.ACTION_PLAY @@ -1874,6 +1881,7 @@ class MediaDataProcessorTest(flags: FlagsParameterization) : SysuiTestCase() { .isNotEqualTo(firstSemanticActions.prevOrCustom?.icon) } + @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_BUTTON_MEDIA3) @Test fun testPlaybackActions_reservedSpace() { val customDesc = arrayOf("custom 1", "custom 2", "custom 3", "custom 4") @@ -1912,6 +1920,7 @@ class MediaDataProcessorTest(flags: FlagsParameterization) : SysuiTestCase() { assertThat(actions.reservePrev).isTrue() } + @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_BUTTON_MEDIA3) @Test fun testPlaybackActions_playPause_hasButton() { val stateActions = PlaybackState.ACTION_PLAY_PAUSE @@ -2074,6 +2083,7 @@ class MediaDataProcessorTest(flags: FlagsParameterization) : SysuiTestCase() { assertThat(mediaDataCaptor.value.semanticActions).isNotNull() } + @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_BUTTON_MEDIA3) @Test fun testPlaybackStateNull_Pause_keyExists_callsListener() { whenever(controller.playbackState).thenReturn(null) @@ -2132,6 +2142,7 @@ class MediaDataProcessorTest(flags: FlagsParameterization) : SysuiTestCase() { assertThat(mediaDataCaptor.value.isClearable).isFalse() } + @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_BUTTON_MEDIA3) @Test fun testRetain_notifPlayer_notifRemoved_setToResume() { fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true) @@ -2162,6 +2173,7 @@ class MediaDataProcessorTest(flags: FlagsParameterization) : SysuiTestCase() { ) } + @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_BUTTON_MEDIA3) @Test fun testRetain_notifPlayer_sessionDestroyed_doesNotChange() { fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true) @@ -2180,6 +2192,7 @@ class MediaDataProcessorTest(flags: FlagsParameterization) : SysuiTestCase() { .onMediaDataLoaded(eq(PACKAGE_NAME), any(), any(), anyBoolean(), anyInt(), anyBoolean()) } + @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_BUTTON_MEDIA3) @Test fun testRetain_notifPlayer_removeWhileActive_fullyRemoved() { fakeFeatureFlags.set(MEDIA_RETAIN_SESSIONS, true) diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt index 8a6df1cbb4de..d88d69da5e59 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt @@ -63,6 +63,7 @@ class DragAndDropTest : SysuiTestCase() { listState = listState, otherTiles = listOf(), columns = 4, + largeTilesSpan = 4, modifier = Modifier.fillMaxSize(), onRemoveTile = {}, onSetTiles = onSetTiles, @@ -75,7 +76,7 @@ class DragAndDropTest : SysuiTestCase() { @Test fun draggedTile_shouldDisappear() { var tiles by mutableStateOf(TestEditTiles) - val listState = EditTileListState(tiles, 4) + val listState = EditTileListState(tiles, columns = 4, largeTilesSpan = 2) composeRule.setContent { EditTileGridUnderTest(listState) { tiles = it.map { tileSpec -> createEditTile(tileSpec.spec) } @@ -101,7 +102,7 @@ class DragAndDropTest : SysuiTestCase() { @Test fun draggedTile_shouldChangePosition() { var tiles by mutableStateOf(TestEditTiles) - val listState = EditTileListState(tiles, 4) + val listState = EditTileListState(tiles, columns = 4, largeTilesSpan = 2) composeRule.setContent { EditTileGridUnderTest(listState) { tiles = it.map { tileSpec -> createEditTile(tileSpec.spec) } @@ -128,7 +129,7 @@ class DragAndDropTest : SysuiTestCase() { @Test fun draggedTileOut_shouldBeRemoved() { var tiles by mutableStateOf(TestEditTiles) - val listState = EditTileListState(tiles, 4) + val listState = EditTileListState(tiles, columns = 4, largeTilesSpan = 2) composeRule.setContent { EditTileGridUnderTest(listState) { tiles = it.map { tileSpec -> createEditTile(tileSpec.spec) } @@ -153,7 +154,7 @@ class DragAndDropTest : SysuiTestCase() { @Test fun draggedNewTileIn_shouldBeAdded() { var tiles by mutableStateOf(TestEditTiles) - val listState = EditTileListState(tiles, 4) + val listState = EditTileListState(tiles, columns = 4, largeTilesSpan = 2) composeRule.setContent { EditTileGridUnderTest(listState) { tiles = it.map { tileSpec -> createEditTile(tileSpec.spec) } 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 d9c1d998798c..fac5ecb49027 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 @@ -62,6 +62,7 @@ class ResizingTest : SysuiTestCase() { listState = listState, otherTiles = listOf(), columns = 4, + largeTilesSpan = 4, modifier = Modifier.fillMaxSize(), onRemoveTile = {}, onSetTiles = {}, @@ -74,7 +75,7 @@ class ResizingTest : SysuiTestCase() { @Test fun toggleIconTile_shouldBeLarge() { var tiles by mutableStateOf(TestEditTiles) - val listState = EditTileListState(tiles, 4) + val listState = EditTileListState(tiles, columns = 4, largeTilesSpan = 2) composeRule.setContent { EditTileGridUnderTest(listState) { spec, toIcon -> tiles = tiles.resize(spec, toIcon) } } @@ -90,7 +91,7 @@ class ResizingTest : SysuiTestCase() { @Test fun toggleLargeTile_shouldBeIcon() { var tiles by mutableStateOf(TestEditTiles) - val listState = EditTileListState(tiles, 4) + val listState = EditTileListState(tiles, columns = 4, largeTilesSpan = 2) composeRule.setContent { EditTileGridUnderTest(listState) { spec, toIcon -> tiles = tiles.resize(spec, toIcon) } } @@ -106,7 +107,7 @@ class ResizingTest : SysuiTestCase() { @Test fun resizedLarge_shouldBeIcon() { var tiles by mutableStateOf(TestEditTiles) - val listState = EditTileListState(tiles, 4) + val listState = EditTileListState(tiles, columns = 4, largeTilesSpan = 2) composeRule.setContent { EditTileGridUnderTest(listState) { spec, toIcon -> tiles = tiles.resize(spec, toIcon) } } @@ -126,7 +127,7 @@ class ResizingTest : SysuiTestCase() { @Test fun resizedIcon_shouldBeLarge() { var tiles by mutableStateOf(TestEditTiles) - val listState = EditTileListState(tiles, 4) + val listState = EditTileListState(tiles, columns = 4, largeTilesSpan = 2) composeRule.setContent { EditTileGridUnderTest(listState) { spec, toIcon -> tiles = tiles.resize(spec, toIcon) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java index 4b648a3e76e7..d1e4f646a382 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java @@ -17,7 +17,6 @@ package com.android.systemui.statusbar.phone.fragment; import static android.view.Display.DEFAULT_DISPLAY; import static com.android.systemui.Flags.FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS; -import static com.android.systemui.Flags.FLAG_STATUS_BAR_SIMPLE_FRAGMENT; import static com.android.systemui.shade.ShadeExpansionStateManagerKt.STATE_CLOSED; import static com.android.systemui.shade.ShadeExpansionStateManagerKt.STATE_OPEN; @@ -61,6 +60,7 @@ import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.OperatorNameViewController; import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips; +import com.android.systemui.statusbar.core.StatusBarRootModernization; import com.android.systemui.statusbar.disableflags.DisableFlagsLogger; import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler; import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerStatusBarViewBinder; @@ -156,7 +156,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { } @Test - @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT) + @DisableFlags(StatusBarRootModernization.FLAG_NAME) public void testDisableNone() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); @@ -167,7 +167,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { } @Test - @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT) + @DisableFlags(StatusBarRootModernization.FLAG_NAME) public void testDisableSystemInfo_systemAnimationIdle_doesHide() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); @@ -185,7 +185,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { } @Test - @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT) + @DisableFlags(StatusBarRootModernization.FLAG_NAME) public void testSystemStatusAnimation_startedDisabled_finishedWithAnimator_showsSystemInfo() { // GIVEN the status bar hides the system info via disable flags, while there is no event CollapsedStatusBarFragment fragment = resumeAndGetFragment(); @@ -215,7 +215,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { } @Test - @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT) + @DisableFlags(StatusBarRootModernization.FLAG_NAME) public void testSystemStatusAnimation_systemInfoDisabled_staysInvisible() { // GIVEN the status bar hides the system info via disable flags, while there is no event CollapsedStatusBarFragment fragment = resumeAndGetFragment(); @@ -232,7 +232,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { } @Test - @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT) + @DisableFlags(StatusBarRootModernization.FLAG_NAME) public void testSystemStatusAnimation_notDisabled_animatesAlphaZero() { // GIVEN the status bar is not disabled CollapsedStatusBarFragment fragment = resumeAndGetFragment(); @@ -248,7 +248,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { } @Test - @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT) + @DisableFlags(StatusBarRootModernization.FLAG_NAME) public void testSystemStatusAnimation_notDisabled_animatesBackToAlphaOne() { // GIVEN the status bar is not disabled CollapsedStatusBarFragment fragment = resumeAndGetFragment(); @@ -272,7 +272,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { } @Test - @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT) + @DisableFlags(StatusBarRootModernization.FLAG_NAME) public void testDisableNotifications() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); @@ -290,7 +290,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { } @Test - @EnableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT) + @EnableFlags(StatusBarRootModernization.FLAG_NAME) public void testDisableNotifications_doesNothingWhenFlagEnabled() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); @@ -308,7 +308,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { } @Test - @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT) + @DisableFlags(StatusBarRootModernization.FLAG_NAME) public void testDisableClock() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); @@ -326,7 +326,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { } @Test - @EnableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT) + @EnableFlags(StatusBarRootModernization.FLAG_NAME) public void testDisableClock_doesNothingWhenFlagEnabled() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); @@ -345,7 +345,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { @Test @DisableSceneContainer - @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT) + @DisableFlags(StatusBarRootModernization.FLAG_NAME) public void disable_shadeOpenAndShouldHide_everythingHidden() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); @@ -363,7 +363,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { @Test @DisableSceneContainer - @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT) + @DisableFlags(StatusBarRootModernization.FLAG_NAME) public void disable_shadeOpenButNotShouldHide_everythingShown() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); @@ -382,7 +382,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { /** Regression test for b/279790651. */ @Test @DisableSceneContainer - @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT) + @DisableFlags(StatusBarRootModernization.FLAG_NAME) public void disable_shadeOpenAndShouldHide_thenShadeNotOpenAndDozingUpdate_everythingShown() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); @@ -410,7 +410,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { } @Test - @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT) + @DisableFlags(StatusBarRootModernization.FLAG_NAME) public void disable_notTransitioningToOccluded_everythingShown() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); @@ -426,7 +426,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { @Test @DisableSceneContainer - @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT) + @DisableFlags(StatusBarRootModernization.FLAG_NAME) public void disable_isTransitioningToOccluded_everythingHidden() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); @@ -442,7 +442,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { @Test @DisableSceneContainer - @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT) + @DisableFlags(StatusBarRootModernization.FLAG_NAME) public void disable_wasTransitioningToOccluded_transitionFinished_everythingShown() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); @@ -473,7 +473,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { } @Test - @DisableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT}) + @DisableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarRootModernization.FLAG_NAME}) public void disable_noOngoingCall_chipHidden() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); @@ -485,7 +485,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { } @Test - @DisableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT}) + @DisableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarRootModernization.FLAG_NAME}) public void disable_hasOngoingCall_chipDisplayedAndNotificationIconsHidden() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); @@ -498,7 +498,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { } @Test - @DisableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT}) + @DisableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarRootModernization.FLAG_NAME}) public void disable_hasOngoingCallButNotificationIconsDisabled_chipHidden() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); @@ -511,7 +511,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { } @Test - @DisableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT}) + @DisableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarRootModernization.FLAG_NAME}) public void disable_hasOngoingCallButAlsoHun_chipHidden() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); @@ -524,7 +524,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { } @Test - @DisableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT}) + @DisableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarRootModernization.FLAG_NAME}) public void disable_ongoingCallEnded_chipHidden() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); @@ -548,7 +548,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { } @Test - @DisableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT}) + @DisableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarRootModernization.FLAG_NAME}) public void disable_hasOngoingCall_hidesNotifsWithoutAnimation() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); // Enable animations for testing so that we can verify we still aren't animating @@ -565,7 +565,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { } @Test - @DisableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, FLAG_STATUS_BAR_SIMPLE_FRAGMENT}) + @DisableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarRootModernization.FLAG_NAME}) public void screenSharingChipsDisabled_ignoresNewCallback() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); @@ -599,7 +599,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { @Test @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS) - @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT) + @DisableFlags(StatusBarRootModernization.FLAG_NAME) public void noOngoingActivity_chipHidden() { resumeAndGetFragment(); @@ -617,7 +617,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { @Test @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS) - @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT) + @DisableFlags(StatusBarRootModernization.FLAG_NAME) public void hasPrimaryOngoingActivity_primaryChipDisplayedAndNotificationIconsHidden() { resumeAndGetFragment(); @@ -634,8 +634,8 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { @EnableFlags({ FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME, - FLAG_STATUS_BAR_SIMPLE_FRAGMENT}) - public void hasPrimaryOngoingActivity_viewsUnchangedWhenSimpleFragmentFlagOn() { + StatusBarRootModernization.FLAG_NAME}) + public void hasPrimaryOngoingActivity_viewsUnchangedWhenRootModernizationFlagOn() { resumeAndGetFragment(); assertEquals(View.VISIBLE, getPrimaryOngoingActivityChipView().getVisibility()); @@ -660,7 +660,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { @Test @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS) - @DisableFlags({StatusBarNotifChips.FLAG_NAME, FLAG_STATUS_BAR_SIMPLE_FRAGMENT}) + @DisableFlags({StatusBarNotifChips.FLAG_NAME, StatusBarRootModernization.FLAG_NAME}) public void hasSecondaryOngoingActivity_butNotifsFlagOff_secondaryChipHidden() { resumeAndGetFragment(); @@ -674,7 +674,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { @Test @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME}) - @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT) + @DisableFlags(StatusBarRootModernization.FLAG_NAME) public void hasSecondaryOngoingActivity_flagOn_secondaryChipShownAndNotificationIconsHidden() { resumeAndGetFragment(); @@ -689,7 +689,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { @Test @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS) - @DisableFlags({StatusBarNotifChips.FLAG_NAME, FLAG_STATUS_BAR_SIMPLE_FRAGMENT}) + @DisableFlags({StatusBarNotifChips.FLAG_NAME, StatusBarRootModernization.FLAG_NAME}) public void hasOngoingActivityButNotificationIconsDisabled_chipHidden_notifsFlagOff() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); @@ -706,7 +706,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { @Test @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME}) - @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT) + @DisableFlags(StatusBarRootModernization.FLAG_NAME) public void hasOngoingActivitiesButNotificationIconsDisabled_chipsHidden_notifsFlagOn() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); @@ -724,7 +724,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { @Test @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS) - @DisableFlags({StatusBarNotifChips.FLAG_NAME, FLAG_STATUS_BAR_SIMPLE_FRAGMENT}) + @DisableFlags({StatusBarNotifChips.FLAG_NAME, StatusBarRootModernization.FLAG_NAME}) public void hasOngoingActivityButAlsoHun_chipHidden_notifsFlagOff() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); @@ -741,7 +741,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { @Test @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME}) - @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT) + @DisableFlags(StatusBarRootModernization.FLAG_NAME) public void hasOngoingActivitiesButAlsoHun_chipsHidden_notifsFlagOn() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); @@ -759,7 +759,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { @Test @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS) - @DisableFlags({StatusBarNotifChips.FLAG_NAME, FLAG_STATUS_BAR_SIMPLE_FRAGMENT}) + @DisableFlags({StatusBarNotifChips.FLAG_NAME, StatusBarRootModernization.FLAG_NAME}) public void primaryOngoingActivityEnded_chipHidden_notifsFlagOff() { resumeAndGetFragment(); @@ -782,7 +782,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { @Test @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME}) - @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT) + @DisableFlags(StatusBarRootModernization.FLAG_NAME) public void primaryOngoingActivityEnded_chipHidden_notifsFlagOn() { resumeAndGetFragment(); @@ -805,7 +805,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { @Test @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME}) - @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT) + @DisableFlags(StatusBarRootModernization.FLAG_NAME) public void secondaryOngoingActivityEnded_chipHidden() { resumeAndGetFragment(); @@ -828,7 +828,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { @Test @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS) - @DisableFlags({StatusBarNotifChips.FLAG_NAME, FLAG_STATUS_BAR_SIMPLE_FRAGMENT}) + @DisableFlags({StatusBarNotifChips.FLAG_NAME, StatusBarRootModernization.FLAG_NAME}) public void hasOngoingActivity_hidesNotifsWithoutAnimation_notifsFlagOff() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); // Enable animations for testing so that we can verify we still aren't animating @@ -847,7 +847,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { @Test @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME}) - @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT) + @DisableFlags(StatusBarRootModernization.FLAG_NAME) public void hasOngoingActivity_hidesNotifsWithoutAnimation_notifsFlagOn() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); // Enable animations for testing so that we can verify we still aren't animating @@ -866,7 +866,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { @Test @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS) - @DisableFlags({StatusBarNotifChips.FLAG_NAME, FLAG_STATUS_BAR_SIMPLE_FRAGMENT}) + @DisableFlags({StatusBarNotifChips.FLAG_NAME, StatusBarRootModernization.FLAG_NAME}) public void screenSharingChipsEnabled_ignoresOngoingCallController_notifsFlagOff() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); @@ -899,7 +899,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { @Test @EnableFlags({FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS, StatusBarNotifChips.FLAG_NAME}) - @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT) + @DisableFlags(StatusBarRootModernization.FLAG_NAME) public void screenSharingChipsEnabled_ignoresOngoingCallController_notifsFlagOn() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); @@ -933,7 +933,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { @Test @EnableSceneContainer - @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT) + @DisableFlags(StatusBarRootModernization.FLAG_NAME) public void isHomeStatusBarAllowedByScene_false_everythingHidden() { resumeAndGetFragment(); @@ -947,7 +947,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { @Test @EnableSceneContainer - @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT) + @DisableFlags(StatusBarRootModernization.FLAG_NAME) public void isHomeStatusBarAllowedByScene_true_everythingShown() { resumeAndGetFragment(); @@ -961,7 +961,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { @Test @EnableSceneContainer - @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT) + @DisableFlags(StatusBarRootModernization.FLAG_NAME) public void disable_isHomeStatusBarAllowedBySceneFalse_disableValuesIgnored() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); @@ -979,7 +979,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { @Test @EnableSceneContainer - @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT) + @DisableFlags(StatusBarRootModernization.FLAG_NAME) public void disable_isHomeStatusBarAllowedBySceneTrue_disableValuesUsed() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); @@ -997,7 +997,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { @Test @DisableSceneContainer - @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT) + @DisableFlags(StatusBarRootModernization.FLAG_NAME) public void isHomeStatusBarAllowedByScene_sceneContainerDisabled_valueNotUsed() { resumeAndGetFragment(); @@ -1011,7 +1011,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { } @Test - @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT) + @DisableFlags(StatusBarRootModernization.FLAG_NAME) public void disable_isDozing_clockAndSystemInfoVisible() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); when(mStatusBarStateController.isDozing()).thenReturn(true); @@ -1023,7 +1023,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { } @Test - @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT) + @DisableFlags(StatusBarRootModernization.FLAG_NAME) public void disable_NotDozing_clockAndSystemInfoVisible() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); when(mStatusBarStateController.isDozing()).thenReturn(false); @@ -1035,7 +1035,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { } @Test - @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT) + @DisableFlags(StatusBarRootModernization.FLAG_NAME) public void disable_headsUpShouldBeVisibleTrue_clockDisabled() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); when(mHeadsUpAppearanceController.shouldBeVisible()).thenReturn(true); @@ -1046,7 +1046,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { } @Test - @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT) + @DisableFlags(StatusBarRootModernization.FLAG_NAME) public void disable_headsUpShouldBeVisibleFalse_clockNotDisabled() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); when(mHeadsUpAppearanceController.shouldBeVisible()).thenReturn(false); @@ -1100,7 +1100,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { @Test @DisableSceneContainer - @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT) + @DisableFlags(StatusBarRootModernization.FLAG_NAME) public void testStatusBarIcons_hiddenThroughoutCameraLaunch() { final CollapsedStatusBarFragment fragment = resumeAndGetFragment(); @@ -1123,7 +1123,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { @Test @DisableSceneContainer - @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT) + @DisableFlags(StatusBarRootModernization.FLAG_NAME) public void testStatusBarIcons_hiddenThroughoutLockscreenToDreamTransition() { final CollapsedStatusBarFragment fragment = resumeAndGetFragment(); @@ -1159,7 +1159,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { } @Test - @DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT) + @DisableFlags(StatusBarRootModernization.FLAG_NAME) public void testStatusBarIcons_lockscreenToDreamTransitionButNotDreaming_iconsVisible() { final CollapsedStatusBarFragment fragment = resumeAndGetFragment(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/MultiSourceMinAlphaControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/MultiSourceMinAlphaControllerTest.kt index c435d3d99680..37671e0bc175 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/MultiSourceMinAlphaControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/MultiSourceMinAlphaControllerTest.kt @@ -21,9 +21,9 @@ import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.view.View import androidx.test.filters.SmallTest -import com.android.systemui.Flags.FLAG_STATUS_BAR_SIMPLE_FRAGMENT import com.android.systemui.SysuiTestCase import com.android.systemui.animation.AnimatorTestRule +import com.android.systemui.statusbar.core.StatusBarRootModernization import junit.framework.Assert.assertEquals import org.junit.Before import org.junit.Rule @@ -38,7 +38,7 @@ private const val INITIAL_ALPHA = 1f @RunWith(AndroidTestingRunner::class) @TestableLooper.RunWithLooper(setAsMainLooper = true) @SmallTest -@DisableFlags(FLAG_STATUS_BAR_SIMPLE_FRAGMENT) +@DisableFlags(StatusBarRootModernization.FLAG_NAME) class MultiSourceMinAlphaControllerTest : SysuiTestCase() { private val view = View(context) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt index c48898aad087..2e0b7c69f092 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt @@ -67,12 +67,12 @@ import com.android.systemui.statusbar.pipeline.shared.data.repository.Connectivi import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository import com.android.systemui.statusbar.pipeline.wifi.data.repository.prod.WifiRepositoryImpl import com.android.systemui.testKosmos +import com.android.systemui.user.data.repository.fakeUserRepository +import com.android.systemui.user.data.repository.userRepository import com.android.systemui.util.concurrency.FakeExecutor -import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.argumentCaptor import com.android.systemui.util.mockito.capture import com.android.systemui.util.mockito.eq -import com.android.systemui.util.mockito.mock import com.android.systemui.util.time.FakeSystemClock import com.android.wifitrackerlib.MergedCarrierEntry import com.android.wifitrackerlib.WifiEntry @@ -96,6 +96,8 @@ import org.mockito.ArgumentMatchers.anyString import org.mockito.Mock import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations +import org.mockito.kotlin.any +import org.mockito.kotlin.mock import org.mockito.kotlin.whenever @Suppress("EXPERIMENTAL_IS_NOT_ENABLED") @@ -137,6 +139,7 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() { private val wifiPickerTrackerCallback = argumentCaptor<WifiPickerTracker.WifiPickerTrackerCallback>() private val vcnTransportInfo = VcnTransportInfo.Builder().build() + private val userRepository = kosmos.fakeUserRepository private val testDispatcher = StandardTestDispatcher() private val testScope = TestScope(testDispatcher) @@ -159,7 +162,14 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() { logcatTableLogBuffer(kosmos, "test") } - whenever(wifiPickerTrackerFactory.create(any(), capture(wifiPickerTrackerCallback), any())) + whenever( + wifiPickerTrackerFactory.create( + any(), + any(), + capture(wifiPickerTrackerCallback), + any(), + ) + ) .thenReturn(wifiPickerTracker) // For convenience, set up the subscription info callbacks @@ -188,6 +198,8 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() { wifiRepository = WifiRepositoryImpl( + mContext, + userRepository, testScope.backgroundScope, mainExecutor, testDispatcher, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt index 44e1437b909e..d823bf57c824 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt @@ -16,13 +16,19 @@ package com.android.systemui.statusbar.pipeline.wifi.data.repository.prod +import android.content.Context +import android.content.pm.UserInfo import android.net.wifi.ScanResult import android.net.wifi.WifiManager import android.net.wifi.WifiManager.UNKNOWN_SSID import android.net.wifi.sharedconnectivity.app.NetworkProviderInfo +import android.os.UserHandle +import android.platform.test.annotations.DisableFlags +import android.platform.test.annotations.EnableFlags import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID import android.testing.TestableLooper import androidx.test.filters.SmallTest +import com.android.systemui.Flags.FLAG_MULTIUSER_WIFI_PICKER_TRACKER_SUPPORT import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.log.LogBuffer @@ -33,12 +39,10 @@ import com.android.systemui.statusbar.pipeline.wifi.data.repository.prod.WifiRep import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiScanEntry import com.android.systemui.testKosmos +import com.android.systemui.user.data.repository.fakeUserRepository +import com.android.systemui.user.data.repository.userRepository import com.android.systemui.util.concurrency.FakeExecutor -import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.argumentCaptor -import com.android.systemui.util.mockito.capture -import com.android.systemui.util.mockito.mock -import com.android.systemui.util.mockito.whenever import com.android.systemui.util.time.fakeSystemClock import com.android.wifitrackerlib.HotspotNetworkEntry import com.android.wifitrackerlib.HotspotNetworkEntry.DeviceType @@ -56,7 +60,12 @@ import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test -import org.mockito.Mockito.verify +import org.mockito.kotlin.any +import org.mockito.kotlin.capture +import org.mockito.kotlin.mock +import org.mockito.kotlin.times +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever /** * Note: Most of these tests are duplicates of [WifiRepositoryImplTest] tests. @@ -67,8 +76,9 @@ import org.mockito.Mockito.verify @OptIn(ExperimentalCoroutinesApi::class) @SmallTest @TestableLooper.RunWithLooper(setAsMainLooper = true) -class WifiRepositoryImplTest : SysuiTestCase() { +class WifiRepositoryImplTest() : SysuiTestCase() { private val kosmos = testKosmos() + private val userRepository = kosmos.fakeUserRepository // Using lazy means that the class will only be constructed once it's fetched. Because the // repository internally sets some values on construction, we need to set up some test @@ -76,6 +86,8 @@ class WifiRepositoryImplTest : SysuiTestCase() { // inside each test case without needing to manually recreate the repository. private val underTest: WifiRepositoryImpl by lazy { WifiRepositoryImpl( + mContext, + userRepository, testScope.backgroundScope, executor, dispatcher, @@ -101,7 +113,8 @@ class WifiRepositoryImplTest : SysuiTestCase() { @Before fun setUp() { - whenever(wifiPickerTrackerFactory.create(any(), capture(callbackCaptor), any())) + userRepository.setUserInfos(listOf(PRIMARY_USER, ANOTHER_USER)) + whenever(wifiPickerTrackerFactory.create(any(), any(), capture(callbackCaptor), any())) .thenReturn(wifiPickerTracker) } @@ -1203,6 +1216,95 @@ class WifiRepositoryImplTest : SysuiTestCase() { assertThat(latest).isEmpty() } + // TODO(b/371586248): This test currently require currentUserContext to be public for testing, + // this needs to + // be updated to capture the argument instead so currentUserContext can be private. + @Test + @EnableFlags(FLAG_MULTIUSER_WIFI_PICKER_TRACKER_SUPPORT) + fun oneUserVerifyCreatingWifiPickerTracker_multiuserFlagEnabled() = + testScope.runTest { + val primaryUserMockContext = mock<Context>() + mContext.prepareCreateContextAsUser( + UserHandle.of(PRIMARY_USER_ID), + primaryUserMockContext, + ) + + userRepository.setSelectedUserInfo(PRIMARY_USER) + runCurrent() + val currentUserContext by collectLastValue(underTest.selectedUserContext) + + assertThat(currentUserContext).isEqualTo(primaryUserMockContext) + verify(wifiPickerTrackerFactory).create(any(), any(), any(), any()) + } + + // TODO(b/371586248): This test currently require currentUserContext to be public for testing, + // this needs to + // be updated to capture the argument instead so currentUserContext can be private. + @Test + @EnableFlags(FLAG_MULTIUSER_WIFI_PICKER_TRACKER_SUPPORT) + fun changeUserVerifyCreatingWifiPickerTracker_multiuserEnabled() = + testScope.runTest { + val primaryUserMockContext = mock<Context>() + mContext.prepareCreateContextAsUser( + UserHandle.of(PRIMARY_USER_ID), + primaryUserMockContext, + ) + + runCurrent() + userRepository.setSelectedUserInfo(PRIMARY_USER) + runCurrent() + val currentUserContext by collectLastValue(underTest.selectedUserContext) + + assertThat(currentUserContext).isEqualTo(primaryUserMockContext) + + val otherUserMockContext = mock<Context>() + mContext.prepareCreateContextAsUser( + UserHandle.of(ANOTHER_USER_ID), + otherUserMockContext, + ) + + runCurrent() + userRepository.setSelectedUserInfo(ANOTHER_USER) + runCurrent() + val otherUserContext by collectLastValue(underTest.selectedUserContext) + + assertThat(otherUserContext).isEqualTo(otherUserMockContext) + verify(wifiPickerTrackerFactory, times(2)).create(any(), any(), any(), any()) + } + + // TODO(b/371586248): This test currently require currentUserContext to be public for testing, + // this needs to + // be updated to capture the argument instead so currentUserContext can be private. + @Test + @DisableFlags(FLAG_MULTIUSER_WIFI_PICKER_TRACKER_SUPPORT) + fun changeUserVerifyCreatingWifiPickerTracker_multiuserDisabled() = + testScope.runTest { + val primaryUserMockContext = mock<Context>() + mContext.prepareCreateContextAsUser( + UserHandle.of(PRIMARY_USER_ID), + primaryUserMockContext, + ) + + runCurrent() + userRepository.setSelectedUserInfo(PRIMARY_USER) + runCurrent() + val currentUserContext by collectLastValue(underTest.selectedUserContext) + + assertThat(currentUserContext).isEqualTo(primaryUserMockContext) + + val otherUserMockContext = mock<Context>() + mContext.prepareCreateContextAsUser( + UserHandle.of(ANOTHER_USER_ID), + otherUserMockContext, + ) + + runCurrent() + userRepository.setSelectedUserInfo(ANOTHER_USER) + runCurrent() + + verify(wifiPickerTrackerFactory, times(1)).create(any(), any(), any(), any()) + } + private fun getCallback(): WifiPickerTracker.WifiPickerTrackerCallback { testScope.runCurrent() return callbackCaptor.value @@ -1231,5 +1333,20 @@ class WifiRepositoryImplTest : SysuiTestCase() { private companion object { const val TITLE = "AB" + private const val PRIMARY_USER_ID = 0 + private val PRIMARY_USER = + UserInfo( + /* id= */ PRIMARY_USER_ID, + /* name= */ "primary user", + /* flags= */ UserInfo.FLAG_PROFILE, + ) + + private const val ANOTHER_USER_ID = 1 + private val ANOTHER_USER = + UserInfo( + /* id= */ ANOTHER_USER_ID, + /* name= */ "another user", + /* flags= */ UserInfo.FLAG_PROFILE, + ) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java index 38a61fecdc8a..21adeb01487b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java @@ -316,6 +316,31 @@ public class WalletScreenControllerTest extends SysuiTestCase { } @Test + public void queryCards_hasCards_showCarousel_invalidIconSource_noIcon() { + GetWalletCardsResponse response = + new GetWalletCardsResponse( + Collections.singletonList(createWalletCardWithInvalidIcon(mContext)), 0); + + mController.queryWalletCards(); + mTestableLooper.processAllMessages(); + + verify(mWalletClient).getWalletCards(any(), any(), mCallbackCaptor.capture()); + + QuickAccessWalletClient.OnWalletCardsRetrievedCallback callback = + mCallbackCaptor.getValue(); + + assertEquals(mController, callback); + + callback.onWalletCardsRetrieved(response); + mTestableLooper.processAllMessages(); + + assertEquals(VISIBLE, mWalletView.getCardCarousel().getVisibility()); + assertEquals(GONE, mWalletView.getEmptyStateView().getVisibility()); + assertEquals(GONE, mWalletView.getErrorView().getVisibility()); + assertEquals(null, mWalletView.getIcon().getDrawable()); + } + + @Test public void queryCards_noCards_showEmptyState() { GetWalletCardsResponse response = new GetWalletCardsResponse(Collections.EMPTY_LIST, 0); @@ -507,6 +532,16 @@ public class WalletScreenControllerTest extends SysuiTestCase { .build(); } + private WalletCard createWalletCardWithInvalidIcon(Context context) { + PendingIntent pendingIntent = + PendingIntent.getActivity(context, 0, mWalletIntent, PendingIntent.FLAG_IMMUTABLE); + return new WalletCard.Builder( + CARD_ID_1, createIconWithInvalidSource(), "•••• 1234", pendingIntent) + .setCardIcon(createIconWithInvalidSource()) + .setCardLabel("Hold to reader") + .build(); + } + private WalletCard createCrazyWalletCard(Context context, boolean hasLabel) { PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, mWalletIntent, PendingIntent.FLAG_IMMUTABLE); @@ -520,6 +555,10 @@ public class WalletScreenControllerTest extends SysuiTestCase { return Icon.createWithBitmap(Bitmap.createBitmap(70, 44, Bitmap.Config.ARGB_8888)); } + private static Icon createIconWithInvalidSource() { + return Icon.createWithContentUri("content://media/external/images/media"); + } + private WalletCardViewInfo createCardViewInfo(WalletCard walletCard) { return new WalletScreenController.QAWalletCardViewInfo( mContext, walletCard); 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 48106de5225b..856333ea724e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java @@ -49,6 +49,7 @@ import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -2395,7 +2396,8 @@ public class BubblesTest extends SysuiTestCase { FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener(); mBubbleController.registerBubbleStateListener(bubbleStateListener); - mBubbleController.setBubbleBarLocation(BubbleBarLocation.LEFT); + mBubbleController.setBubbleBarLocation(BubbleBarLocation.LEFT, + BubbleBarLocation.UpdateSource.DRAG_EXP_VIEW); assertThat(bubbleStateListener.mLastUpdate).isNotNull(); assertThat(bubbleStateListener.mLastUpdate.bubbleBarLocation).isEqualTo( BubbleBarLocation.LEFT); @@ -2408,7 +2410,8 @@ public class BubblesTest extends SysuiTestCase { FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener(); mBubbleController.registerBubbleStateListener(bubbleStateListener); - mBubbleController.setBubbleBarLocation(BubbleBarLocation.LEFT); + mBubbleController.setBubbleBarLocation(BubbleBarLocation.LEFT, + BubbleBarLocation.UpdateSource.DRAG_EXP_VIEW); assertThat(bubbleStateListener.mStateChangeCalls).isEqualTo(0); } @@ -2505,17 +2508,54 @@ public class BubblesTest extends SysuiTestCase { @EnableFlags(FLAG_ENABLE_BUBBLE_BAR) @Test - public void testEventLogging_bubbleBar_dragBubbleToDismiss() { + public void testEventLogging_bubbleBar_dragSelectedBubbleToDismiss() { mBubbleProperties.mIsBubbleBarEnabled = true; mPositioner.setIsLargeScreen(true); FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener(); mBubbleController.registerBubbleStateListener(bubbleStateListener); mEntryListener.onEntryAdded(mRow); - mBubbleController.dragBubbleToDismiss(mRow.getKey(), 1L); + mEntryListener.onEntryAdded(mRow2); + mBubbleController.expandStackAndSelectBubbleFromLauncher(mRow2.getKey(), 0); + + clearInvocations(mBubbleLogger); + + // Dismiss selected bubble + mBubbleController.startBubbleDrag(mRow2.getKey()); + mBubbleController.dragBubbleToDismiss(mRow2.getKey(), System.currentTimeMillis()); + // Log bubble dismissed via drag and new bubble selected + verify(mBubbleLogger).log(eqBubbleWithKey(mRow2.getKey()), + eq(BubbleLogger.Event.BUBBLE_BAR_BUBBLE_DISMISSED_DRAG_BUBBLE)); + verify(mBubbleLogger).log(eqBubbleWithKey(mRow.getKey()), + eq(BubbleLogger.Event.BUBBLE_BAR_BUBBLE_SWITCHED)); + + verifyNoMoreInteractions(mBubbleLogger); + } + + @EnableFlags(FLAG_ENABLE_BUBBLE_BAR) + @Test + public void testEventLogging_bubbleBar_dragOtherBubbleToDismiss() { + mBubbleProperties.mIsBubbleBarEnabled = true; + mPositioner.setIsLargeScreen(true); + FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener(); + mBubbleController.registerBubbleStateListener(bubbleStateListener); + + mEntryListener.onEntryAdded(mRow); + mEntryListener.onEntryAdded(mRow2); + mBubbleController.expandStackAndSelectBubbleFromLauncher(mRow2.getKey(), 0); + + clearInvocations(mBubbleLogger); + + // Dismiss other bubble + mBubbleController.startBubbleDrag(mRow.getKey()); + mBubbleController.dragBubbleToDismiss(mRow.getKey(), System.currentTimeMillis()); + + // Log bubble dismissed via drag, but no switch event verify(mBubbleLogger).log(eqBubbleWithKey(mRow.getKey()), eq(BubbleLogger.Event.BUBBLE_BAR_BUBBLE_DISMISSED_DRAG_BUBBLE)); + + verifyNoMoreInteractions(mBubbleLogger); } @EnableFlags(FLAG_ENABLE_BUBBLE_BAR) @@ -2535,6 +2575,78 @@ public class BubblesTest extends SysuiTestCase { @EnableFlags(FLAG_ENABLE_BUBBLE_BAR) @Test + public void testEventLogging_bubbleBar_dragBarLeft() { + mBubbleProperties.mIsBubbleBarEnabled = true; + mPositioner.setIsLargeScreen(true); + mPositioner.setBubbleBarLocation(BubbleBarLocation.RIGHT); + FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener(); + mBubbleController.registerBubbleStateListener(bubbleStateListener); + + mEntryListener.onEntryAdded(mRow); + assertBarMode(); + + mBubbleController.setBubbleBarLocation(BubbleBarLocation.LEFT, + BubbleBarLocation.UpdateSource.DRAG_BAR); + + verify(mBubbleLogger).log(BubbleLogger.Event.BUBBLE_BAR_MOVED_LEFT_DRAG_BAR); + } + + @EnableFlags(FLAG_ENABLE_BUBBLE_BAR) + @Test + public void testEventLogging_bubbleBar_dragBarRight() { + mBubbleProperties.mIsBubbleBarEnabled = true; + mPositioner.setIsLargeScreen(true); + mPositioner.setBubbleBarLocation(BubbleBarLocation.LEFT); + FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener(); + mBubbleController.registerBubbleStateListener(bubbleStateListener); + + mEntryListener.onEntryAdded(mRow); + assertBarMode(); + + mBubbleController.setBubbleBarLocation(BubbleBarLocation.RIGHT, + BubbleBarLocation.UpdateSource.DRAG_BAR); + + verify(mBubbleLogger).log(BubbleLogger.Event.BUBBLE_BAR_MOVED_RIGHT_DRAG_BAR); + } + + @EnableFlags(FLAG_ENABLE_BUBBLE_BAR) + @Test + public void testEventLogging_bubbleBar_dragBubbleLeft() { + mBubbleProperties.mIsBubbleBarEnabled = true; + mPositioner.setIsLargeScreen(true); + mPositioner.setBubbleBarLocation(BubbleBarLocation.RIGHT); + FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener(); + mBubbleController.registerBubbleStateListener(bubbleStateListener); + + mEntryListener.onEntryAdded(mRow); + assertBarMode(); + + mBubbleController.setBubbleBarLocation(BubbleBarLocation.LEFT, + BubbleBarLocation.UpdateSource.DRAG_BUBBLE); + + verify(mBubbleLogger).log(BubbleLogger.Event.BUBBLE_BAR_MOVED_LEFT_DRAG_BUBBLE); + } + + @EnableFlags(FLAG_ENABLE_BUBBLE_BAR) + @Test + public void testEventLogging_bubbleBar_dragBubbleRight() { + mBubbleProperties.mIsBubbleBarEnabled = true; + mPositioner.setIsLargeScreen(true); + mPositioner.setBubbleBarLocation(BubbleBarLocation.LEFT); + FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener(); + mBubbleController.registerBubbleStateListener(bubbleStateListener); + + mEntryListener.onEntryAdded(mRow); + assertBarMode(); + + mBubbleController.setBubbleBarLocation(BubbleBarLocation.RIGHT, + BubbleBarLocation.UpdateSource.DRAG_BUBBLE); + + verify(mBubbleLogger).log(BubbleLogger.Event.BUBBLE_BAR_MOVED_RIGHT_DRAG_BUBBLE); + } + + @EnableFlags(FLAG_ENABLE_BUBBLE_BAR) + @Test public void testEventLogging_bubbleBar_expandAndCollapse() { mBubbleProperties.mIsBubbleBarEnabled = true; mPositioner.setIsLargeScreen(true); @@ -2569,6 +2681,32 @@ public class BubblesTest extends SysuiTestCase { eq(BubbleLogger.Event.BUBBLE_BAR_EXPANDED)); } + @EnableFlags(FLAG_ENABLE_BUBBLE_BAR) + @Test + public void testEventLogging_bubbleBar_switchBubble() { + mBubbleProperties.mIsBubbleBarEnabled = true; + mPositioner.setIsLargeScreen(true); + FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener(); + mBubbleController.registerBubbleStateListener(bubbleStateListener); + + mEntryListener.onEntryAdded(mRow); + mEntryListener.onEntryAdded(mRow2); + mBubbleController.expandStackAndSelectBubbleFromLauncher(mRow.getKey(), 0); + + // First select is expand + verify(mBubbleLogger).log(eqBubbleWithKey(mRow.getKey()), + eq(BubbleLogger.Event.BUBBLE_BAR_EXPANDED)); + verify(mBubbleLogger, never()).log(eqBubbleWithKey(mRow.getKey()), + eq(BubbleLogger.Event.BUBBLE_BAR_BUBBLE_SWITCHED)); + + // Second select is switch + mBubbleController.expandStackAndSelectBubbleFromLauncher(mRow2.getKey(), 0); + verify(mBubbleLogger).log(eqBubbleWithKey(mRow2.getKey()), + eq(BubbleLogger.Event.BUBBLE_BAR_BUBBLE_SWITCHED)); + verify(mBubbleLogger, never()).log(eqBubbleWithKey(mRow2.getKey()), + eq(BubbleLogger.Event.BUBBLE_BAR_EXPANDED)); + } + /** Creates a bubble using the userId and package. */ private Bubble createBubble(int userId, String pkg) { final UserHandle userHandle = new UserHandle(userId); diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt index 219794f3ad18..a7917a0866bb 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt @@ -37,9 +37,7 @@ import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.currentTime -class FakeAuthenticationRepository( - private val currentTime: () -> Long, -) : AuthenticationRepository { +class FakeAuthenticationRepository(private val currentTime: () -> Long) : AuthenticationRepository { override val hintedPinLength: Int = HINTING_PIN_LENGTH @@ -72,6 +70,9 @@ class FakeAuthenticationRepository( private val credentialCheckingMutex = Mutex(locked = false) + var maximumTimeToLock: Long = 0 + var powerButtonInstantlyLocks: Boolean = true + override suspend fun getAuthenticationMethod(): AuthenticationMethodModel { return authenticationMethod.value } @@ -114,6 +115,7 @@ class FakeAuthenticationRepository( MAX_FAILED_AUTH_TRIES_BEFORE_WIPE var profileWithMinFailedUnlockAttemptsForWipe: Int = UserHandle.USER_SYSTEM + override suspend fun getProfileWithMinFailedUnlockAttemptsForWipe(): Int = profileWithMinFailedUnlockAttemptsForWipe @@ -144,10 +146,7 @@ class FakeAuthenticationRepository( val failedAttempts = _failedAuthenticationAttempts.value if (isSuccessful || failedAttempts < MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT - 1) { - AuthenticationResultModel( - isSuccessful = isSuccessful, - lockoutDurationMs = 0, - ) + AuthenticationResultModel(isSuccessful = isSuccessful, lockoutDurationMs = 0) } else { AuthenticationResultModel( isSuccessful = false, @@ -178,6 +177,14 @@ class FakeAuthenticationRepository( credentialCheckingMutex.unlock() } + override suspend fun getMaximumTimeToLock(): Long { + return maximumTimeToLock + } + + override suspend fun getPowerButtonInstantlyLocks(): Boolean { + return powerButtonInstantlyLocks + } + private fun getExpectedCredential(securityMode: SecurityMode): List<Any> { return when (val credentialType = getCurrentCredentialType(securityMode)) { LockPatternUtils.CREDENTIAL_TYPE_PIN -> credentialOverride ?: DEFAULT_PIN @@ -219,9 +226,7 @@ class FakeAuthenticationRepository( } @LockPatternUtils.CredentialType - private fun getCurrentCredentialType( - securityMode: SecurityMode, - ): Int { + private fun getCurrentCredentialType(securityMode: SecurityMode): Int { return when (securityMode) { SecurityMode.PIN, SecurityMode.SimPin, @@ -260,9 +265,8 @@ class FakeAuthenticationRepository( object FakeAuthenticationRepositoryModule { @Provides @SysUISingleton - fun provideFake( - scope: TestScope, - ) = FakeAuthenticationRepository(currentTime = { scope.currentTime }) + fun provideFake(scope: TestScope) = + FakeAuthenticationRepository(currentTime = { scope.currentTime }) @Module interface Bindings { diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryRepository.kt index 2dcd275f0103..f6ff4c4a057e 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/FakeDeviceEntryRepository.kt @@ -16,6 +16,7 @@ package com.android.systemui.deviceentry.data.repository import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.deviceentry.shared.model.DeviceUnlockStatus import dagger.Binds import dagger.Module import javax.inject.Inject @@ -35,6 +36,9 @@ class FakeDeviceEntryRepository @Inject constructor() : DeviceEntryRepository { private var pendingLockscreenEnabled = _isLockscreenEnabled.value + override val deviceUnlockStatus = + MutableStateFlow(DeviceUnlockStatus(isUnlocked = false, deviceUnlockSource = null)) + override suspend fun isLockscreenEnabled(): Boolean { _isLockscreenEnabled.value = pendingLockscreenEnabled return isLockscreenEnabled.value diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractorKosmos.kt index 8922b2f5c5ef..e4c7df64fdc6 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractorKosmos.kt @@ -19,25 +19,27 @@ package com.android.systemui.deviceentry.domain.interactor import com.android.systemui.authentication.domain.interactor.authenticationInteractor import com.android.systemui.deviceentry.data.repository.deviceEntryRepository import com.android.systemui.flags.fakeSystemPropertiesHelper -import com.android.systemui.flags.systemPropertiesHelper -import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor +import com.android.systemui.keyguard.domain.interactor.keyguardInteractor import com.android.systemui.keyguard.domain.interactor.trustInteractor import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture -import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.kosmos.testScope +import com.android.systemui.lifecycle.activateIn import com.android.systemui.power.domain.interactor.powerInteractor +import com.android.systemui.util.settings.data.repository.userAwareSecureSettingsRepository val Kosmos.deviceUnlockedInteractor by Fixture { DeviceUnlockedInteractor( - applicationScope = applicationCoroutineScope, - authenticationInteractor = authenticationInteractor, - deviceEntryRepository = deviceEntryRepository, - trustInteractor = trustInteractor, - faceAuthInteractor = deviceEntryFaceAuthInteractor, - fingerprintAuthInteractor = deviceEntryFingerprintAuthInteractor, - powerInteractor = powerInteractor, - biometricSettingsInteractor = deviceEntryBiometricSettingsInteractor, - systemPropertiesHelper = fakeSystemPropertiesHelper, - keyguardTransitionInteractor = keyguardTransitionInteractor, - ) + authenticationInteractor = authenticationInteractor, + repository = deviceEntryRepository, + trustInteractor = trustInteractor, + faceAuthInteractor = deviceEntryFaceAuthInteractor, + fingerprintAuthInteractor = deviceEntryFingerprintAuthInteractor, + powerInteractor = powerInteractor, + biometricSettingsInteractor = deviceEntryBiometricSettingsInteractor, + systemPropertiesHelper = fakeSystemPropertiesHelper, + userAwareSecureSettingsRepository = userAwareSecureSettingsRepository, + keyguardInteractor = keyguardInteractor, + ) + .apply { activateIn(testScope) } } 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 8022e6e86a49..f52f039b6758 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 @@ -22,7 +22,9 @@ import android.content.res.mainResources import android.hardware.input.fakeInputManager import android.view.windowManager import com.android.systemui.broadcast.broadcastDispatcher -import com.android.systemui.keyboard.shortcut.data.repository.ShortcutHelperCategoriesRepository +import com.android.systemui.keyboard.shortcut.data.repository.CustomShortcutCategoriesRepository +import com.android.systemui.keyboard.shortcut.data.repository.DefaultShortcutCategoriesRepository +import com.android.systemui.keyboard.shortcut.data.repository.ShortcutCategoriesUtils import com.android.systemui.keyboard.shortcut.data.repository.ShortcutHelperStateRepository import com.android.systemui.keyboard.shortcut.data.repository.ShortcutHelperTestHelper import com.android.systemui.keyboard.shortcut.data.source.AppCategoriesShortcutsSource @@ -40,6 +42,7 @@ import com.android.systemui.keyboard.shortcut.ui.viewmodel.ShortcutHelperViewMod import com.android.systemui.keyguard.data.repository.fakeCommandQueue import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.kosmos.backgroundCoroutineContext import com.android.systemui.kosmos.testDispatcher import com.android.systemui.kosmos.testScope import com.android.systemui.model.sysUiState @@ -73,10 +76,18 @@ var Kosmos.shortcutHelperInputShortcutsSource: KeyboardShortcutGroupsSource by var Kosmos.shortcutHelperCurrentAppShortcutsSource: KeyboardShortcutGroupsSource by Kosmos.Fixture { CurrentAppShortcutsSource(windowManager) } -val Kosmos.shortcutHelperCategoriesRepository by +val Kosmos.shortcutCategoriesUtils by Kosmos.Fixture { - ShortcutHelperCategoriesRepository( + ShortcutCategoriesUtils( applicationContext, + backgroundCoroutineContext, + fakeInputManager.inputManager, + ) + } + +val Kosmos.defaultShortcutCategoriesRepository by + Kosmos.Fixture { + DefaultShortcutCategoriesRepository( applicationCoroutineScope, testDispatcher, shortcutHelperSystemShortcutsSource, @@ -86,6 +97,18 @@ val Kosmos.shortcutHelperCategoriesRepository by shortcutHelperCurrentAppShortcutsSource, fakeInputManager.inputManager, shortcutHelperStateRepository, + shortcutCategoriesUtils, + ) + } + +val Kosmos.customShortcutCategoriesRepository by + Kosmos.Fixture { + CustomShortcutCategoriesRepository( + shortcutHelperStateRepository, + userTracker, + applicationCoroutineScope, + testDispatcher, + shortcutCategoriesUtils, ) } @@ -112,7 +135,7 @@ val Kosmos.shortcutHelperStateInteractor by } val Kosmos.shortcutHelperCategoriesInteractor by - Kosmos.Fixture { ShortcutHelperCategoriesInteractor(shortcutHelperCategoriesRepository) } + Kosmos.Fixture { ShortcutHelperCategoriesInteractor(defaultShortcutCategoriesRepository) } val Kosmos.shortcutHelperViewModel by Kosmos.Fixture { diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt index 19e077c57de0..8209ee12ad9a 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt @@ -87,7 +87,7 @@ class FakeKeyguardTransitionRepository( ) : this( initInLockscreen = true, initiallySendTransitionStepsOnStartTransition = true, - testScope + testScope, ) private val _currentTransitionInfo: MutableStateFlow<TransitionInfo> = @@ -191,12 +191,12 @@ class FakeKeyguardTransitionRepository( if (lastStep != null && lastStep.transitionState != TransitionState.FINISHED) { sendTransitionStep( step = - TransitionStep( - transitionState = TransitionState.CANCELED, - from = lastStep.from, - to = lastStep.to, - value = 0f, - ) + TransitionStep( + transitionState = TransitionState.CANCELED, + from = lastStep.from, + to = lastStep.to, + value = 0f, + ) ) testScheduler.runCurrent() } @@ -390,6 +390,18 @@ class FakeKeyguardTransitionRepository( @FloatRange(from = 0.0, to = 1.0) value: Float, state: TransitionState, ) = Unit + + override suspend fun forceFinishCurrentTransition() { + _transitions.tryEmit( + TransitionStep( + _currentTransitionInfo.value.from, + _currentTransitionInfo.value.to, + 1f, + TransitionState.FINISHED, + ownerName = _currentTransitionInfo.value.ownerName, + ) + ) + } } @Module diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt index aa94c368e8f1..b9a831f11d23 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt @@ -19,6 +19,7 @@ package com.android.systemui.keyguard.domain.interactor import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.power.domain.interactor.powerInteractor import com.android.systemui.scene.domain.interactor.sceneInteractor val Kosmos.keyguardTransitionInteractor: KeyguardTransitionInteractor by @@ -26,6 +27,7 @@ val Kosmos.keyguardTransitionInteractor: KeyguardTransitionInteractor by KeyguardTransitionInteractor( scope = applicationCoroutineScope, repository = keyguardTransitionRepository, - sceneInteractor = sceneInteractor + sceneInteractor = sceneInteractor, + powerInteractor = powerInteractor, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/Media3ActionFactoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/Media3ActionFactoryKosmos.kt index f49e3771763a..b3be2c09c6f8 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/Media3ActionFactoryKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/domain/pipeline/Media3ActionFactoryKosmos.kt @@ -22,6 +22,7 @@ import android.os.Handler import android.os.looper import androidx.media3.session.CommandButton import androidx.media3.session.MediaController +import androidx.media3.session.SessionCommand import androidx.media3.session.SessionToken import com.android.systemui.Flags import com.android.systemui.graphics.imageLoader @@ -30,7 +31,11 @@ import com.android.systemui.kosmos.testScope import com.android.systemui.media.controls.shared.mediaLogger import com.android.systemui.media.controls.util.fakeMediaControllerFactory import com.android.systemui.media.controls.util.fakeSessionTokenFactory +import com.android.systemui.util.concurrency.execution import com.google.common.collect.ImmutableList +import org.mockito.kotlin.any +import org.mockito.kotlin.argumentCaptor +import org.mockito.kotlin.doAnswer import org.mockito.kotlin.mock import org.mockito.kotlin.whenever @@ -46,10 +51,22 @@ var Kosmos.media3ActionFactory: Media3ActionFactory by mock<MediaController>().also { whenever(it.customLayout).thenReturn(customLayout) whenever(it.sessionExtras).thenReturn(Bundle()) + whenever(it.isCommandAvailable(any())).thenReturn(true) + whenever(it.isSessionCommandAvailable(any<SessionCommand>())).thenReturn(true) } fakeMediaControllerFactory.setMedia3Controller(media3Controller) fakeSessionTokenFactory.setMedia3SessionToken(mock<SessionToken>()) } + + val runnableCaptor = argumentCaptor<Runnable>() + val handler = + mock<Handler> { + on { post(runnableCaptor.capture()) } doAnswer + { + runnableCaptor.lastValue.run() + true + } + } Media3ActionFactory( context = applicationContext, imageLoader = imageLoader, @@ -57,7 +74,8 @@ var Kosmos.media3ActionFactory: Media3ActionFactory by tokenFactory = fakeSessionTokenFactory, logger = mediaLogger, looper = looper, - handler = Handler(looper), + handler = handler, bgScope = testScope, + execution = execution, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/LargeTileSpanRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/LargeTileSpanRepositoryKosmos.kt new file mode 100644 index 000000000000..a977121b3803 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/LargeTileSpanRepositoryKosmos.kt @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.panels.data.repository + +import android.content.res.mainResources +import com.android.systemui.common.ui.data.repository.configurationRepository +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.applicationCoroutineScope + +val Kosmos.largeTileSpanRepository by + Kosmos.Fixture { + LargeTileSpanRepository(applicationCoroutineScope, mainResources, configurationRepository) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/QSPreferencesRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/QSPreferencesRepositoryKosmos.kt index 513d4e418a41..1395b1818b30 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/QSPreferencesRepositoryKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/QSPreferencesRepositoryKosmos.kt @@ -16,8 +16,10 @@ package com.android.systemui.qs.panels.data.repository +import com.android.systemui.broadcast.broadcastDispatcher import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.testDispatcher +import com.android.systemui.log.core.FakeLogBuffer import com.android.systemui.settings.userFileManager import com.android.systemui.user.data.repository.userRepository @@ -27,6 +29,8 @@ val Kosmos.qsPreferencesRepository by userFileManager, userRepository, defaultLargeTilesRepository, - testDispatcher + testDispatcher, + FakeLogBuffer.Factory.create(), + broadcastDispatcher, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractorKosmos.kt index 0c62d0e85ce1..8d4db8b74061 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractorKosmos.kt @@ -20,6 +20,7 @@ import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.log.core.FakeLogBuffer import com.android.systemui.qs.panels.data.repository.defaultLargeTilesRepository +import com.android.systemui.qs.panels.data.repository.largeTileSpanRepository import com.android.systemui.qs.pipeline.domain.interactor.currentTilesInteractor val Kosmos.iconTilesInteractor by @@ -28,7 +29,8 @@ val Kosmos.iconTilesInteractor by defaultLargeTilesRepository, currentTilesInteractor, qsPreferencesInteractor, + largeTileSpanRepository, FakeLogBuffer.Factory.create(), - applicationCoroutineScope + applicationCoroutineScope, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/MediaInRowInLandscapeViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/MediaInRowInLandscapeViewModelKosmos.kt index d1b613fe7f6e..f63698a3f2f9 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/MediaInRowInLandscapeViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/MediaInRowInLandscapeViewModelKosmos.kt @@ -21,6 +21,7 @@ import android.content.res.mainResources import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository import com.android.systemui.common.ui.domain.interactor.configurationInteractor import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.runCurrent import com.android.systemui.media.controls.ui.controller.mediaHostStatesManager import com.android.systemui.qs.composefragment.dagger.usingMediaInComposeFragment import com.android.systemui.shade.data.repository.shadeRepository @@ -56,4 +57,5 @@ fun Kosmos.setConfigurationForMediaInRow(mediaInRow: Boolean) { } mainResources.configuration.updateFrom(config) fakeConfigurationRepository.onConfigurationChange(config) + runCurrent() } 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 45aab860cde7..28edae7c3689 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 @@ -49,6 +49,7 @@ val Kosmos.statusBarOrchestrator by fakeStatusBarModePerDisplayRepository, fakeStatusBarInitializer, fakeStatusBarWindowController, + applicationCoroutineScope.coroutineContext, mockDemoModeController, mockPluginDependencyProvider, mockAutoHideController, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/events/data/repository/SystemStatusEventAnimationRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/events/data/repository/SystemStatusEventAnimationRepositoryKosmos.kt new file mode 100644 index 000000000000..92eeef97f7c4 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/events/data/repository/SystemStatusEventAnimationRepositoryKosmos.kt @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.events.data.repository + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState +import kotlinx.coroutines.flow.MutableStateFlow + +val Kosmos.systemStatusEventAnimationRepository: FakeSystemStatusEventAnimationRepository by + Kosmos.Fixture { FakeSystemStatusEventAnimationRepository() } + +class FakeSystemStatusEventAnimationRepository : SystemStatusEventAnimationRepository { + override val animationState = MutableStateFlow(SystemEventAnimationState.Idle) +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/events/domain/interactor/SystemStatusEventAnimationInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/events/domain/interactor/SystemStatusEventAnimationInteractorKosmos.kt new file mode 100644 index 000000000000..7513fead0187 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/events/domain/interactor/SystemStatusEventAnimationInteractorKosmos.kt @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.events.domain.interactor + +import com.android.systemui.common.ui.domain.interactor.configurationInteractor +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.statusbar.events.data.repository.systemStatusEventAnimationRepository + +val Kosmos.systemStatusEventAnimationInteractor by + Kosmos.Fixture { + SystemStatusEventAnimationInteractor( + repo = systemStatusEventAnimationRepository, + configurationInteractor = configurationInteractor, + scope = applicationCoroutineScope, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/model/ActiveNotificationModelBuilder.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/model/ActiveNotificationModelBuilder.kt index 32c582f79ed7..2ec801620212 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/model/ActiveNotificationModelBuilder.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/model/ActiveNotificationModelBuilder.kt @@ -19,6 +19,7 @@ package com.android.systemui.statusbar.notification.data.model import android.app.PendingIntent import android.graphics.drawable.Icon import com.android.systemui.statusbar.StatusBarIconView +import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel import com.android.systemui.statusbar.notification.shared.CallType import com.android.systemui.statusbar.notification.stack.BUCKET_UNKNOWN @@ -46,6 +47,7 @@ fun activeNotificationModel( contentIntent: PendingIntent? = null, bucket: Int = BUCKET_UNKNOWN, callType: CallType = CallType.None, + promotedContent: PromotedNotificationContentModel? = null, ) = ActiveNotificationModel( key = key, @@ -69,4 +71,5 @@ fun activeNotificationModel( isGroupSummary = isGroupSummary, bucket = bucket, callType = callType, + promotedContent = promotedContent, ) diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt index c3c3cce5cf68..dae66d42b2bc 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt @@ -41,6 +41,7 @@ class FakeMobileConnectionRepository( override val isGsm = MutableStateFlow(false) override val cdmaLevel = MutableStateFlow(0) override val primaryLevel = MutableStateFlow(0) + override val satelliteLevel = MutableStateFlow(0) override val dataConnectionState = MutableStateFlow(DataConnectionState.Disconnected) override val dataActivityDirection = MutableStateFlow(DataActivityModel(hasActivityIn = false, hasActivityOut = false)) 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 3a7ada2e61b8..03e4c894c2f2 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 @@ -24,6 +24,7 @@ import com.android.systemui.scene.domain.interactor.sceneContainerOcclusionInter import com.android.systemui.scene.domain.interactor.sceneInteractor 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.phone.domain.interactor.lightsOutInteractor import com.android.systemui.statusbar.pipeline.shared.domain.interactor.collapsedStatusBarInteractor @@ -40,6 +41,7 @@ val Kosmos.homeStatusBarViewModel: HomeStatusBarViewModel by sceneContainerOcclusionInteractor, shadeInteractor, ongoingActivityChipsViewModel, + systemStatusEventAnimationInteractor, applicationCoroutineScope, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt index 1808a5f99f4e..85d582a27faf 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt @@ -72,6 +72,10 @@ class FakeUserRepository @Inject constructor() : UserRepository { override val isSecondaryUserLogoutEnabled: StateFlow<Boolean> = _isSecondaryUserLogoutEnabled.asStateFlow() + private val _isLogoutToSystemUserEnabled = MutableStateFlow<Boolean>(false) + override val isLogoutToSystemUserEnabled: StateFlow<Boolean> = + _isLogoutToSystemUserEnabled.asStateFlow() + override var mainUserId: Int = MAIN_USER_ID override var lastSelectedNonGuestUserId: Int = mainUserId @@ -123,6 +127,17 @@ class FakeUserRepository @Inject constructor() : UserRepository { logOutSecondaryUserCallCount++ } + fun setLogoutToSystemUserEnabled(logoutEnabled: Boolean) { + _isLogoutToSystemUserEnabled.value = logoutEnabled + } + + var logOutToSystemUserCallCount: Int = 0 + private set + + override suspend fun logOutToSystemUser() { + logOutToSystemUserCallCount++ + } + fun setUserInfos(infos: List<UserInfo>) { _userInfos.value = infos } diff --git a/services/core/java/com/android/server/integrity/model/IndexingFileConstants.java b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ringer/data/repository/FakeVolumeDialogRingerFeedbackRepository.kt index 0c4052adc3ed..d42de1e6d0df 100644 --- a/services/core/java/com/android/server/integrity/model/IndexingFileConstants.java +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ringer/data/repository/FakeVolumeDialogRingerFeedbackRepository.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 The Android Open Source Project + * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,14 +14,17 @@ * limitations under the License. */ -package com.android.server.integrity.model; +package com.android.systemui.volume.dialog.ringer.data.repository -/** A helper class containing special indexing file constants. */ -public final class IndexingFileConstants { - // We empirically experimented with different block sizes and identified that 50 is in the - // optimal range of efficient computation. - public static final int INDEXING_BLOCK_SIZE = 50; +class FakeVolumeDialogRingerFeedbackRepository : VolumeDialogRingerFeedbackRepository { - public static final String START_INDEXING_KEY = "START_KEY"; - public static final String END_INDEXING_KEY = "END_KEY"; + private var seenToastCount = 0 + + override suspend fun getToastCount(): Int { + return seenToastCount + } + + override suspend fun updateToastCount(toastCount: Int) { + seenToastCount = toastCount + } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ringer/data/repository/VolumeDialogRingerFeedbackRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ringer/data/repository/VolumeDialogRingerFeedbackRepositoryKosmos.kt new file mode 100644 index 000000000000..44371b4615df --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ringer/data/repository/VolumeDialogRingerFeedbackRepositoryKosmos.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.volume.dialog.ringer.data.repository + +import com.android.systemui.kosmos.Kosmos + +val Kosmos.fakeVolumeDialogRingerFeedbackRepository by + Kosmos.Fixture { FakeVolumeDialogRingerFeedbackRepository() } +val Kosmos.volumeDialogRingerFeedbackRepository by + Kosmos.Fixture { fakeVolumeDialogRingerFeedbackRepository } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ringer/domain/VolumeDialogRingerInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ringer/domain/VolumeDialogRingerInteractorKosmos.kt index 1addd91d2ec2..a494d04ec741 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ringer/domain/VolumeDialogRingerInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ringer/domain/VolumeDialogRingerInteractorKosmos.kt @@ -21,6 +21,7 @@ import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.plugins.volumeDialogController import com.android.systemui.volume.data.repository.audioSystemRepository import com.android.systemui.volume.dialog.domain.interactor.volumeDialogStateInteractor +import com.android.systemui.volume.dialog.ringer.data.repository.fakeVolumeDialogRingerFeedbackRepository val Kosmos.volumeDialogRingerInteractor by Kosmos.Fixture { @@ -29,5 +30,6 @@ val Kosmos.volumeDialogRingerInteractor by volumeDialogStateInteractor = volumeDialogStateInteractor, controller = volumeDialogController, audioSystemRepository = audioSystemRepository, + ringerFeedbackRepository = fakeVolumeDialogRingerFeedbackRepository, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModelKosmos.kt index db1c01a8698c..c8ba551c518a 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModelKosmos.kt @@ -16,20 +16,24 @@ package com.android.systemui.volume.dialog.ringer.ui.viewmodel +import android.content.applicationContext import com.android.systemui.haptics.vibratorHelper import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.kosmos.testDispatcher +import com.android.systemui.volume.dialog.domain.interactor.volumeDialogVisibilityInteractor import com.android.systemui.volume.dialog.ringer.domain.volumeDialogRingerInteractor import com.android.systemui.volume.dialog.shared.volumeDialogLogger val Kosmos.volumeDialogRingerDrawerViewModel by Kosmos.Fixture { VolumeDialogRingerDrawerViewModel( + applicationContext = applicationContext, backgroundDispatcher = testDispatcher, coroutineScope = applicationCoroutineScope, interactor = volumeDialogRingerInteractor, vibrator = vibratorHelper, volumeDialogLogger = volumeDialogLogger, + visibilityInteractor = volumeDialogVisibilityInteractor, ) } diff --git a/ravenwood/TEST_MAPPING b/ravenwood/TEST_MAPPING index 607592b4cbbe..cb54e9f56c0c 100644 --- a/ravenwood/TEST_MAPPING +++ b/ravenwood/TEST_MAPPING @@ -113,10 +113,6 @@ "host": true }, { - "name": "FrameworksMockingServicesTestsRavenwood", - "host": true - }, - { "name": "FrameworksServicesTestsRavenwood_Compat", "host": true }, diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java index d8f2b705d539..3ed8b0a748e1 100644 --- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java +++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java @@ -16,7 +16,6 @@ package android.platform.test.ravenwood; import static android.os.Process.FIRST_APPLICATION_UID; -import static android.os.Process.SYSTEM_UID; import static android.os.UserHandle.SYSTEM; import android.annotation.NonNull; @@ -61,17 +60,14 @@ public final class RavenwoodConfig { * Unless the test author requests differently, run as "nobody", and give each collection of * tests its own unique PID. */ - int mUid = NOBODY_UID; + int mUid = FIRST_APPLICATION_UID; int mPid = sNextPid.getAndIncrement(); String mTestPackageName; String mTargetPackageName; - int mMinSdkLevel; int mTargetSdkLevel = Build.VERSION_CODES.CUR_DEVELOPMENT; - boolean mProvideMainThread = false; - final RavenwoodSystemProperties mSystemProperties = new RavenwoodSystemProperties(); final List<Class<?>> mServicesRequired = new ArrayList<>(); @@ -108,20 +104,18 @@ public final class RavenwoodConfig { } /** - * Configure the identity of this process to be the system UID for the duration of the - * test. Has no effect on non-Ravenwood environments. + * @deprecated no longer used. We always use an app UID. */ + @Deprecated public Builder setProcessSystem() { - mConfig.mUid = SYSTEM_UID; return this; } /** - * Configure the identity of this process to be an app UID for the duration of the - * test. Has no effect on non-Ravenwood environments. + * @deprecated no longer used. We always use an app UID. */ + @Deprecated public Builder setProcessApp() { - mConfig.mUid = FIRST_APPLICATION_UID; return this; } @@ -144,14 +138,6 @@ public final class RavenwoodConfig { } /** - * Configure the min SDK level of the test. - */ - public Builder setMinSdkLevel(int sdkLevel) { - mConfig.mMinSdkLevel = sdkLevel; - return this; - } - - /** * Configure the target SDK level of the test. */ public Builder setTargetSdkLevel(int sdkLevel) { @@ -160,14 +146,10 @@ public final class RavenwoodConfig { } /** - * Configure a "main" thread to be available for the duration of the test, as defined - * by {@code Looper.getMainLooper()}. Has no effect on non-Ravenwood environments. - * - * @deprecated + * @deprecated no longer used. Main thread is always available. */ @Deprecated public Builder setProvideMainThread(boolean provideMainThread) { - mConfig.mProvideMainThread = provideMainThread; 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 3d6ac0f37050..bfa3802ce583 100644 --- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java +++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java @@ -112,20 +112,18 @@ public final class RavenwoodRule implements TestRule { } /** - * Configure the identity of this process to be the system UID for the duration of the - * test. Has no effect on non-Ravenwood environments. + * @deprecated no longer used. We always use an app UID. */ + @Deprecated public Builder setProcessSystem() { - mBuilder.setProcessSystem(); return this; } /** - * Configure the identity of this process to be an app UID for the duration of the - * test. Has no effect on non-Ravenwood environments. + * @deprecated no longer used. We always use an app UID. */ + @Deprecated public Builder setProcessApp() { - mBuilder.setProcessApp(); return this; } @@ -139,14 +137,10 @@ public final class RavenwoodRule implements TestRule { } /** - * Configure a "main" thread to be available for the duration of the test, as defined - * by {@code Looper.getMainLooper()}. Has no effect on non-Ravenwood environments. - * - * @deprecated + * @deprecated no longer used. Main thread is always available. */ @Deprecated public Builder setProvideMainThread(boolean provideMainThread) { - mBuilder.setProvideMainThread(provideMainThread); return this; } diff --git a/ravenwood/runtime-helper-src/framework/android/util/Log_host.java b/ravenwood/runtime-helper-src/framework/android/util/Log_host.java index d232ef2076be..c85bd23db893 100644 --- a/ravenwood/runtime-helper-src/framework/android/util/Log_host.java +++ b/ravenwood/runtime-helper-src/framework/android/util/Log_host.java @@ -18,6 +18,7 @@ package android.util; import android.util.Log.Level; import com.android.internal.os.RuntimeInit; +import com.android.ravenwood.common.RavenwoodCommonUtils; import java.io.PrintStream; @@ -35,6 +36,9 @@ public class Log_host { } public static int println_native(int bufID, int priority, String tag, String msg) { + if (priority < Log.INFO && !RavenwoodCommonUtils.RAVENWOOD_VERBOSE_LOGGING) { + return msg.length(); // No verbose logging. + } final String buffer; switch (bufID) { case Log.LOG_ID_MAIN: buffer = "main"; break; diff --git a/ravenwood/texts/ravenwood-annotation-allowed-classes.txt b/ravenwood/texts/ravenwood-annotation-allowed-classes.txt index 82be2c0db24e..c196a09e18d1 100644 --- a/ravenwood/texts/ravenwood-annotation-allowed-classes.txt +++ b/ravenwood/texts/ravenwood-annotation-allowed-classes.txt @@ -15,7 +15,9 @@ com.android.internal.os.BatteryStatsHistory com.android.internal.os.BatteryStatsHistoryIterator com.android.internal.os.Clock com.android.internal.os.LongArrayMultiStateCounter +com.android.internal.os.LongArrayMultiStateCounter_ravenwood com.android.internal.os.LongMultiStateCounter +com.android.internal.os.LongMultiStateCounter_ravenwood com.android.internal.os.MonotonicClock com.android.internal.os.PowerProfile com.android.internal.os.PowerStats @@ -143,6 +145,7 @@ android.os.LocaleList android.os.Looper android.os.Message android.os.MessageQueue +android.os.MessageQueue_ravenwood android.os.PackageTagsList android.os.Parcel android.os.ParcelFileDescriptor @@ -235,6 +238,7 @@ android.database.Cursor android.database.CursorIndexOutOfBoundsException android.database.CursorJoiner android.database.CursorWindow +android.database.CursorWindow_ravenwood android.database.CursorWrapper android.database.DataSetObservable android.database.DataSetObserver @@ -370,4 +374,3 @@ android.app.compat.* com.android.server.compat.* com.android.internal.compat.* android.app.AppCompatCallbacks - diff --git a/ravenwood/tools/hoststubgen/test-tiny-framework/diff-and-update-golden.sh b/ravenwood/tools/hoststubgen/test-tiny-framework/diff-and-update-golden.sh index 3726ca972564..b389a67a8e4c 100755 --- a/ravenwood/tools/hoststubgen/test-tiny-framework/diff-and-update-golden.sh +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/diff-and-update-golden.sh @@ -30,7 +30,7 @@ help() { EOF } -source "${0%/*}"/../../common.sh +source "${0%/*}"/../common.sh SCRIPT_NAME="${0##*/}" @@ -61,7 +61,6 @@ esac done shift $(($OPTIND - 1)) - # Build the dump files, which are the input of this test. run m dump-jar tiny-framework-dump-test diff --git a/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py index cee29dcd1d59..7a7de3553829 100755 --- a/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py @@ -47,6 +47,7 @@ class TestWithGoldenOutput(unittest.TestCase): # Test to check the generated jar files to the golden output. def test_compare_to_golden(self): + self.skipTest("test cannot handle multiple images (see b/378470825)") files = os.listdir(GOLDEN_DIR) files.sort() diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java index 617cca9d3075..d6fc6e461edc 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java @@ -27,6 +27,8 @@ import android.annotation.NonNull; import android.content.Context; import android.graphics.Region; import android.hardware.input.InputManager; +import android.hardware.input.KeyGestureEvent; +import android.os.IBinder; import android.os.Looper; import android.os.PowerManager; import android.os.SystemClock; @@ -44,11 +46,14 @@ import android.view.MotionEvent.PointerCoords; import android.view.MotionEvent.PointerProperties; import android.view.accessibility.AccessibilityEvent; +import androidx.annotation.Nullable; + import com.android.server.LocalServices; import com.android.server.accessibility.gestures.TouchExplorer; import com.android.server.accessibility.magnification.FullScreenMagnificationController; import com.android.server.accessibility.magnification.FullScreenMagnificationGestureHandler; import com.android.server.accessibility.magnification.FullScreenMagnificationVibrationHelper; +import com.android.server.accessibility.magnification.MagnificationController; import com.android.server.accessibility.magnification.MagnificationGestureHandler; import com.android.server.accessibility.magnification.MouseEventHandler; import com.android.server.accessibility.magnification.WindowMagnificationGestureHandler; @@ -187,6 +192,8 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo private final AccessibilityManagerService mAms; + private final InputManager mInputManager; + private final SparseArray<EventStreamTransformation> mEventHandler; private final SparseArray<TouchExplorer> mTouchExplorer = new SparseArray<>(0); @@ -228,6 +235,47 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo */ private MotionEvent mLastActiveDeviceMotionEvent = null; + private boolean mKeyGestureEventHandlerInstalled = false; + private InputManager.KeyGestureEventHandler mKeyGestureEventHandler = + new InputManager.KeyGestureEventHandler() { + @Override + public boolean handleKeyGestureEvent( + @NonNull KeyGestureEvent event, + @Nullable IBinder focusedToken) { + final boolean complete = + event.getAction() == KeyGestureEvent.ACTION_GESTURE_COMPLETE + && !event.isCancelled(); + final int gestureType = event.getKeyGestureType(); + final int displayId = isDisplayIdValid(event.getDisplayId()) + ? event.getDisplayId() : Display.DEFAULT_DISPLAY; + + switch (gestureType) { + case KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_IN: + if (complete) { + mAms.getMagnificationController().scaleMagnificationByStep( + displayId, MagnificationController.ZOOM_DIRECTION_IN); + } + return true; + case KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_OUT: + if (complete) { + mAms.getMagnificationController().scaleMagnificationByStep( + displayId, MagnificationController.ZOOM_DIRECTION_OUT); + } + return true; + } + return false; + } + + @Override + public boolean isKeyGestureSupported(int gestureType) { + return switch (gestureType) { + case KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_IN, + KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_OUT -> true; + default -> false; + }; + } + }; + private static MotionEvent cancelMotion(MotionEvent event) { if (event.getActionMasked() == MotionEvent.ACTION_CANCEL || event.getActionMasked() == MotionEvent.ACTION_HOVER_EXIT @@ -287,6 +335,7 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo mContext = context; mAms = service; mPm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); + mInputManager = context.getSystemService(InputManager.class); mEventHandler = eventHandler; } @@ -723,6 +772,12 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo createMagnificationGestureHandler(displayId, displayContext); addFirstEventHandler(displayId, magnificationGestureHandler); mMagnificationGestureHandler.put(displayId, magnificationGestureHandler); + + if (com.android.hardware.input.Flags.enableTalkbackAndMagnifierKeyGestures() + && !mKeyGestureEventHandlerInstalled) { + mInputManager.registerKeyGestureEventHandler(mKeyGestureEventHandler); + mKeyGestureEventHandlerInstalled = true; + } } if ((mEnabledFeatures & FLAG_FEATURE_INJECT_MOTION_EVENTS) != 0) { @@ -842,6 +897,11 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo mMouseKeysInterceptor.onDestroy(); mMouseKeysInterceptor = null; } + + if (mKeyGestureEventHandlerInstalled) { + mInputManager.unregisterKeyGestureEventHandler(mKeyGestureEventHandler); + mKeyGestureEventHandlerInstalled = false; + } } private MagnificationGestureHandler createMagnificationGestureHandler( diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 974cba2450f4..d4af7b765254 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -42,9 +42,11 @@ import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATIN import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_GESTURE; import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR; import static android.provider.Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED; +import static android.view.Display.INVALID_DISPLAY; import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL; import static android.view.accessibility.AccessibilityManager.FlashNotificationReason; +import static com.android.hardware.input.Flags.enableTalkbackAndMagnifierKeyGestures; import static com.android.hardware.input.Flags.keyboardA11yMouseKeys; import static com.android.internal.accessibility.AccessibilityShortcutController.ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME; import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_COMPONENT_NAME; @@ -55,6 +57,7 @@ import static com.android.internal.accessibility.common.ShortcutConstants.UserSh import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.ALL; 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.KEY_GESTURE; import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.QUICK_SETTINGS; import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE; import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.TRIPLETAP; @@ -111,6 +114,8 @@ import android.graphics.Rect; import android.graphics.Region; import android.hardware.display.DisplayManager; import android.hardware.fingerprint.IFingerprintService; +import android.hardware.input.InputManager; +import android.hardware.input.KeyGestureEvent; import android.media.AudioManagerInternal; import android.net.Uri; import android.os.Binder; @@ -338,6 +343,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub private AlertDialog mEnableTouchExplorationDialog; + private final InputManager mInputManager; + private AccessibilityInputFilter mInputFilter; private boolean mHasInputFilter; @@ -503,6 +510,25 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } + private InputManager.KeyGestureEventHandler mKeyGestureEventHandler = + new InputManager.KeyGestureEventHandler() { + @Override + public boolean handleKeyGestureEvent( + @NonNull KeyGestureEvent event, + @Nullable IBinder focusedToken) { + return AccessibilityManagerService.this.handleKeyGestureEvent(event); + } + + @Override + public boolean isKeyGestureSupported(int gestureType) { + return switch (gestureType) { + case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION, + KeyGestureEvent.KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK -> true; + default -> false; + }; + } + }; + @VisibleForTesting AccessibilityManagerService( Context context, @@ -542,6 +568,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub mUmi = LocalServices.getService(UserManagerInternal.class); // TODO(b/255426725): not used on tests mVisibleBgUserIds = null; + mInputManager = context.getSystemService(InputManager.class); init(); } @@ -583,6 +610,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub mUiAutomationManager, this); mFlashNotificationsController = new FlashNotificationsController(mContext); mUmi = LocalServices.getService(UserManagerInternal.class); + mInputManager = context.getSystemService(InputManager.class); if (UserManager.isVisibleBackgroundUsersEnabled()) { mVisibleBgUserIds = new SparseBooleanArray(); @@ -599,6 +627,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub registerBroadcastReceivers(); new AccessibilityContentObserver(mMainHandler).register( mContext.getContentResolver()); + if (enableTalkbackAndMagnifierKeyGestures()) { + mInputManager.registerKeyGestureEventHandler(mKeyGestureEventHandler); + } disableAccessibilityMenuToMigrateIfNeeded(); } @@ -640,6 +671,79 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return mIsAccessibilityButtonShown; } + @VisibleForTesting + boolean handleKeyGestureEvent(KeyGestureEvent event) { + final boolean complete = + event.getAction() == KeyGestureEvent.ACTION_GESTURE_COMPLETE + && !event.isCancelled(); + final int gestureType = event.getKeyGestureType(); + if (!complete) { + return false; + } + + String targetName; + switch (gestureType) { + case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION: + targetName = MAGNIFICATION_CONTROLLER_NAME; + break; + case KeyGestureEvent.KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK: + targetName = mContext.getString(R.string.config_defaultSelectToSpeakService); + if (targetName.isEmpty()) { + return false; + } + + final ComponentName targetServiceComponent = TextUtils.isEmpty(targetName) + ? null : ComponentName.unflattenFromString(targetName); + AccessibilityServiceInfo accessibilityServiceInfo; + synchronized (mLock) { + AccessibilityUserState userState = getCurrentUserStateLocked(); + accessibilityServiceInfo = + userState.getInstalledServiceInfoLocked(targetServiceComponent); + } + if (accessibilityServiceInfo == null) { + return false; + } + + // Skip enabling if a warning dialog is required for the feature. + // TODO(b/377752960): Explore better options to instead show the warning dialog + // in this scenario. + if (isAccessibilityServiceWarningRequired(accessibilityServiceInfo)) { + Slog.w(LOG_TAG, + "Accessibility warning is required before this service can be " + + "activated automatically via KEY_GESTURE shortcut."); + return false; + } + break; + default: + return false; + } + + List<String> shortcutTargets = getAccessibilityShortcutTargets( + KEY_GESTURE); + if (!shortcutTargets.contains(targetName)) { + int userId; + synchronized (mLock) { + userId = mCurrentUserId; + } + // TODO(b/377752960): Add dialog to confirm enabling the service and to + // activate the first time. + enableShortcutForTargets(true, UserShortcutType.KEY_GESTURE, + List.of(targetName), userId); + + // Do not perform action on first press since it was just registered. Eventually, + // this will be a separate dialog that appears that requires the user to confirm + // which will resolve this race condition. For now, just require two presses the + // first time it is activated. + return true; + } + + final int displayId = event.getDisplayId() != INVALID_DISPLAY + ? event.getDisplayId() : getLastNonProxyTopFocusedDisplayId(); + performAccessibilityShortcutInternal(displayId, KEY_GESTURE, targetName); + + return true; + } + @Override public Pair<float[], MagnificationSpec> getWindowTransformationMatrixAndMagnificationSpec( int windowId) { @@ -1224,14 +1328,14 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub int displayId = event.getDisplayId(); final int windowId = event.getWindowId(); if (windowId != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID - && displayId == Display.INVALID_DISPLAY) { + && displayId == INVALID_DISPLAY) { displayId = mA11yWindowManager.getDisplayIdByUserIdAndWindowId( resolvedUserId, windowId); event.setDisplayId(displayId); } synchronized (mLock) { if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED - && displayId != Display.INVALID_DISPLAY + && displayId != INVALID_DISPLAY && mA11yWindowManager.isTrackingWindowsLocked(displayId)) { shouldComputeWindows = true; } @@ -3257,6 +3361,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub updateAccessibilityShortcutTargetsLocked(userState, SOFTWARE); updateAccessibilityShortcutTargetsLocked(userState, GESTURE); updateAccessibilityShortcutTargetsLocked(userState, QUICK_SETTINGS); + updateAccessibilityShortcutTargetsLocked(userState, KEY_GESTURE); // Update the capabilities before the mode because we will check the current mode is // invalid or not.. updateMagnificationCapabilitiesSettingsChangeLocked(userState); @@ -3387,6 +3492,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub somethingChanged |= readAccessibilityShortcutTargetsLocked(userState, QUICK_SETTINGS); somethingChanged |= readAccessibilityShortcutTargetsLocked(userState, SOFTWARE); somethingChanged |= readAccessibilityShortcutTargetsLocked(userState, GESTURE); + somethingChanged |= readAccessibilityShortcutTargetsLocked(userState, KEY_GESTURE); somethingChanged |= readAccessibilityButtonTargetComponentLocked(userState); somethingChanged |= readUserRecommendedUiTimeoutSettingsLocked(userState); somethingChanged |= readMagnificationModeForDefaultDisplayLocked(userState); @@ -3968,6 +4074,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub if (android.provider.Flags.a11yStandaloneGestureEnabled()) { shortcutTypes.add(GESTURE); } + shortcutTypes.add(KEY_GESTURE); final ComponentName serviceName = service.getComponentName(); for (Integer shortcutType: shortcutTypes) { @@ -4078,13 +4185,15 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub */ private void performAccessibilityShortcutInternal(int displayId, @UserShortcutType int shortcutType, @Nullable String targetName) { - final List<String> shortcutTargets = getAccessibilityShortcutTargetsInternal(shortcutType); + final List<String> shortcutTargets = getAccessibilityShortcutTargetsInternal( + shortcutType); if (shortcutTargets.isEmpty()) { Slog.d(LOG_TAG, "No target to perform shortcut, shortcutType=" + shortcutType); return; } // In case the caller specified a target name - if (targetName != null && !doesShortcutTargetsStringContain(shortcutTargets, targetName)) { + if (targetName != null && !doesShortcutTargetsStringContain(shortcutTargets, + targetName)) { Slog.v(LOG_TAG, "Perform shortcut failed, invalid target name:" + targetName); targetName = null; } @@ -4306,6 +4415,13 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return; } + if (shortcutType == UserShortcutType.KEY_GESTURE + && !enableTalkbackAndMagnifierKeyGestures()) { + Slog.w(LOG_TAG, + "KEY_GESTURE type shortcuts are disabled by feature flag"); + return; + } + final String shortcutTypeSettingKey = ShortcutUtils.convertToKey(shortcutType); if (shortcutType == UserShortcutType.TRIPLETAP || shortcutType == UserShortcutType.TWOFINGER_DOUBLETAP) { @@ -5071,6 +5187,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub @EnforcePermission(MANAGE_ACCESSIBILITY) public boolean isAccessibilityServiceWarningRequired(AccessibilityServiceInfo info) { isAccessibilityServiceWarningRequired_enforcePermission(); + if (info == null) { + Log.e(LOG_TAG, "Called isAccessibilityServiceWarningRequired with null service info"); + return true; + } + final ComponentName componentName = info.getComponentName(); // Warning is not required if the service is already enabled. @@ -5678,6 +5799,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub private final Uri mAccessibilityGestureTargetsUri = Settings.Secure.getUriFor( Settings.Secure.ACCESSIBILITY_GESTURE_TARGETS); + private final Uri mAccessibilityKeyGestureTargetsUri = Settings.Secure.getUriFor( + Settings.Secure.ACCESSIBILITY_KEY_GESTURE_TARGETS); + private final Uri mUserNonInteractiveUiTimeoutUri = Settings.Secure.getUriFor( Settings.Secure.ACCESSIBILITY_NON_INTERACTIVE_UI_TIMEOUT_MS); @@ -5742,6 +5866,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub contentResolver.registerContentObserver( mAccessibilityGestureTargetsUri, false, this, UserHandle.USER_ALL); contentResolver.registerContentObserver( + mAccessibilityKeyGestureTargetsUri, false, this, UserHandle.USER_ALL); + contentResolver.registerContentObserver( mUserNonInteractiveUiTimeoutUri, false, this, UserHandle.USER_ALL); contentResolver.registerContentObserver( mUserInteractiveUiTimeoutUri, false, this, UserHandle.USER_ALL); @@ -5823,6 +5949,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub if (readAccessibilityShortcutTargetsLocked(userState, GESTURE)) { onUserStateChangedLocked(userState); } + } else if (mAccessibilityKeyGestureTargetsUri.equals(uri)) { + if (readAccessibilityShortcutTargetsLocked(userState, KEY_GESTURE)) { + onUserStateChangedLocked(userState); + } } else if (mUserNonInteractiveUiTimeoutUri.equals(uri) || mUserInteractiveUiTimeoutUri.equals(uri)) { readUserRecommendedUiTimeoutSettingsLocked(userState); diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java index 67b40632dde8..8b3e63d0dc5e 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java @@ -29,6 +29,7 @@ import static com.android.internal.accessibility.AccessibilityShortcutController import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType; 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.KEY_GESTURE; import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.QUICK_SETTINGS; import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE; import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.TRIPLETAP; @@ -209,6 +210,7 @@ class AccessibilityUserState { mShortcutTargets.put(SOFTWARE, new ArraySet<>()); mShortcutTargets.put(GESTURE, new ArraySet<>()); mShortcutTargets.put(QUICK_SETTINGS, new ArraySet<>()); + mShortcutTargets.put(KEY_GESTURE, new ArraySet<>()); } boolean isHandlingAccessibilityEventsLocked() { 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 d40e7476f7ec..51c4305061f8 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java @@ -27,6 +27,7 @@ import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ import static com.android.server.accessibility.AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID; import android.accessibilityservice.MagnificationConfig; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; @@ -101,6 +102,7 @@ public class MagnificationController implements MagnificationConnectionManager.C private int mMagnificationCapabilities = ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN; /** Whether the platform supports window magnification feature. */ private final boolean mSupportWindowMagnification; + private final MagnificationScaleStepProvider mScaleStepProvider; private final Executor mBackgroundExecutor; @@ -131,6 +133,14 @@ public class MagnificationController implements MagnificationConnectionManager.C .UiChangesForAccessibilityCallbacks> mAccessibilityCallbacksDelegateArray = new SparseArray<>(); + // Direction magnifier scale can be altered. + public static final int ZOOM_DIRECTION_IN = 0; + public static final int ZOOM_DIRECTION_OUT = 1; + + @IntDef({ZOOM_DIRECTION_IN, ZOOM_DIRECTION_OUT}) + public @interface ZoomDirection { + } + /** * A callback to inform the magnification transition result on the given display. */ @@ -144,6 +154,41 @@ public class MagnificationController implements MagnificationConnectionManager.C void onResult(int displayId, boolean success); } + + /** + * An interface to configure how much the magnification scale should be affected when moving in + * steps. + */ + public interface MagnificationScaleStepProvider { + /** + * Calculate the next value given which direction (in/out) to adjust the magnification + * scale. + * + * @param currentScale The current magnification scale value. + * @param direction Whether to zoom in or out. + * @return The next scale value. + */ + float nextScaleStep(float currentScale, @ZoomDirection int direction); + } + + public static class DefaultMagnificationScaleStepProvider implements + MagnificationScaleStepProvider { + // Factor of magnification scale. For example, when this value is 1.189, scale + // value will be changed x1.000, x1.189, x1.414, x1.681, x2.000, ... + // Note: this value is 2.0 ^ (1 / 4). + public static final float ZOOM_STEP_SCALE_FACTOR = 1.18920712f; + + @Override + public float nextScaleStep(float currentScale, @ZoomDirection int direction) { + final int stepDelta = direction == ZOOM_DIRECTION_IN ? 1 : -1; + final long scaleIndex = Math.round( + Math.log(currentScale) / Math.log(ZOOM_STEP_SCALE_FACTOR)); + final float nextScale = (float) Math.pow(ZOOM_STEP_SCALE_FACTOR, + scaleIndex + stepDelta); + return MagnificationScaleProvider.constrainScale(nextScale); + } + } + public MagnificationController(AccessibilityManagerService ams, Object lock, Context context, MagnificationScaleProvider scaleProvider, Executor backgroundExecutor) { @@ -156,6 +201,7 @@ public class MagnificationController implements MagnificationConnectionManager.C .getAccessibilityController().setUiChangesForAccessibilityCallbacks(this); mSupportWindowMagnification = context.getPackageManager().hasSystemFeature( FEATURE_WINDOW_MAGNIFICATION); + mScaleStepProvider = new DefaultMagnificationScaleStepProvider(); mAlwaysOnMagnificationFeatureFlag = new AlwaysOnMagnificationFeatureFlag(context); mAlwaysOnMagnificationFeatureFlag.addOnChangedListener( @@ -891,6 +937,37 @@ public class MagnificationController implements MagnificationConnectionManager.C return isActivated; } + /** + * Scales the magnifier on the given display one step in/out based on the zoomIn param. + * + * @param displayId The logical display id. + * @param direction Whether the scale should be zoomed in or out. + * @return {@code true} if the magnification scale was affected. + */ + public boolean scaleMagnificationByStep(int displayId, @ZoomDirection int direction) { + if (getFullScreenMagnificationController().isActivated(displayId)) { + final float magnificationScale = getFullScreenMagnificationController().getScale( + displayId); + final float nextMagnificationScale = mScaleStepProvider.nextScaleStep( + magnificationScale, direction); + getFullScreenMagnificationController().setScaleAndCenter(displayId, + nextMagnificationScale, + Float.NaN, Float.NaN, true, MAGNIFICATION_GESTURE_HANDLER_ID); + return nextMagnificationScale != magnificationScale; + } + + if (getMagnificationConnectionManager().isWindowMagnifierEnabled(displayId)) { + final float magnificationScale = getMagnificationConnectionManager().getScale( + displayId); + final float nextMagnificationScale = mScaleStepProvider.nextScaleStep( + magnificationScale, direction); + getMagnificationConnectionManager().setScale(displayId, nextMagnificationScale); + return nextMagnificationScale != magnificationScale; + } + + return false; + } + private final class DisableMagnificationCallback implements MagnificationAnimationCallback { private final TransitionCallBack mTransitionCallBack; diff --git a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionExecutors.java b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionExecutors.java index 44ae1d1fbbbf..81e83b563945 100644 --- a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionExecutors.java +++ b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionExecutors.java @@ -16,7 +16,6 @@ package com.android.server.appfunctions; -import java.util.concurrent.Executor; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; @@ -24,15 +23,21 @@ import java.util.concurrent.TimeUnit; /** Executors for App function operations. */ public final class AppFunctionExecutors { + static final int sConcurrency = Runtime.getRuntime().availableProcessors(); + /** Executor for operations that do not need to block. */ - public static final Executor THREAD_POOL_EXECUTOR = + public static final ThreadPoolExecutor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor( - /* corePoolSize= */ Runtime.getRuntime().availableProcessors(), - /* maxConcurrency= */ Runtime.getRuntime().availableProcessors(), - /* keepAliveTime= */ 0L, + /* corePoolSize= */ sConcurrency, + /* maxConcurrency= */ sConcurrency, + /* keepAliveTime= */ 1L, /* unit= */ TimeUnit.SECONDS, /* workQueue= */ new LinkedBlockingQueue<>(), new NamedThreadFactory("AppFunctionExecutors")); + static { + THREAD_POOL_EXECUTOR.allowCoreThreadTimeOut(true); + } + private AppFunctionExecutors() {} } diff --git a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java index 268e56487c4b..f13e22950e2d 100644 --- a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java +++ b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java @@ -29,7 +29,7 @@ import android.app.appfunctions.AppFunctionManagerHelper; import android.app.appfunctions.AppFunctionRuntimeMetadata; import android.app.appfunctions.AppFunctionStaticMetadataHelper; import android.app.appfunctions.ExecuteAppFunctionAidlRequest; -import android.app.appfunctions.ExecuteAppFunctionResponse; +import android.app.appfunctions.AppFunctionException; import android.app.appfunctions.IAppFunctionEnabledCallback; import android.app.appfunctions.IAppFunctionManager; import android.app.appfunctions.IAppFunctionService; @@ -156,11 +156,10 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub { mCallerValidator.verifyTargetUserHandle( requestInternal.getUserHandle(), validatedCallingPackage); } catch (SecurityException exception) { - safeExecuteAppFunctionCallback.onResult( - ExecuteAppFunctionResponse.newFailure( - ExecuteAppFunctionResponse.RESULT_DENIED, - exception.getMessage(), - /* extras= */ null)); + safeExecuteAppFunctionCallback.onError( + new AppFunctionException( + AppFunctionException.ERROR_DENIED, + exception.getMessage())); return null; } @@ -180,7 +179,7 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub { safeExecuteAppFunctionCallback, executeAppFunctionCallback.asBinder()); } catch (Exception e) { - safeExecuteAppFunctionCallback.onResult( + safeExecuteAppFunctionCallback.onError( mapExceptionToExecuteAppFunctionResponse(e)); } }); @@ -198,22 +197,19 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub { UserHandle targetUser = requestInternal.getUserHandle(); // TODO(b/354956319): Add and honor the new enterprise policies. if (mCallerValidator.isUserOrganizationManaged(targetUser)) { - safeExecuteAppFunctionCallback.onResult( - ExecuteAppFunctionResponse.newFailure( - ExecuteAppFunctionResponse.RESULT_SYSTEM_ERROR, + safeExecuteAppFunctionCallback.onError( + new AppFunctionException(AppFunctionException.ERROR_SYSTEM_ERROR, "Cannot run on a device with a device owner or from the managed" - + " profile.", - /* extras= */ null)); + + " profile.")); return; } String targetPackageName = requestInternal.getClientRequest().getTargetPackageName(); if (TextUtils.isEmpty(targetPackageName)) { - safeExecuteAppFunctionCallback.onResult( - ExecuteAppFunctionResponse.newFailure( - ExecuteAppFunctionResponse.RESULT_INVALID_ARGUMENT, - "Target package name cannot be empty.", - /* extras= */ null)); + safeExecuteAppFunctionCallback.onError( + new AppFunctionException( + AppFunctionException.ERROR_INVALID_ARGUMENT, + "Target package name cannot be empty.")); return; } @@ -253,11 +249,10 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub { mInternalServiceHelper.resolveAppFunctionService( targetPackageName, targetUser); if (serviceIntent == null) { - safeExecuteAppFunctionCallback.onResult( - ExecuteAppFunctionResponse.newFailure( - ExecuteAppFunctionResponse.RESULT_SYSTEM_ERROR, - "Cannot find the target service.", - /* extras= */ null)); + safeExecuteAppFunctionCallback.onError( + new AppFunctionException( + AppFunctionException.ERROR_SYSTEM_ERROR, + "Cannot find the target service.")); return; } bindAppFunctionServiceUnchecked( @@ -272,7 +267,7 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub { }) .exceptionally( ex -> { - safeExecuteAppFunctionCallback.onResult( + safeExecuteAppFunctionCallback.onError( mapExceptionToExecuteAppFunctionResponse(ex)); return null; }); @@ -446,11 +441,9 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub { if (!bindServiceResult) { Slog.e(TAG, "Failed to bind to the AppFunctionService"); - safeExecuteAppFunctionCallback.onResult( - ExecuteAppFunctionResponse.newFailure( - ExecuteAppFunctionResponse.RESULT_SYSTEM_ERROR, - "Failed to bind the AppFunctionService.", - /* extras= */ null)); + safeExecuteAppFunctionCallback.onError( + new AppFunctionException(AppFunctionException.ERROR_SYSTEM_ERROR, + "Failed to bind the AppFunctionService.")); } } @@ -459,22 +452,21 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub { .getSystemService(AppSearchManager.class); } - private ExecuteAppFunctionResponse mapExceptionToExecuteAppFunctionResponse(Throwable e) { + private AppFunctionException mapExceptionToExecuteAppFunctionResponse(Throwable e) { if (e instanceof CompletionException) { e = e.getCause(); } - int resultCode = ExecuteAppFunctionResponse.RESULT_SYSTEM_ERROR; + int resultCode = AppFunctionException.ERROR_SYSTEM_ERROR; if (e instanceof AppSearchException appSearchException) { resultCode = mapAppSearchResultFailureCodeToExecuteAppFunctionResponse( appSearchException.getResultCode()); } else if (e instanceof SecurityException) { - resultCode = ExecuteAppFunctionResponse.RESULT_DENIED; + resultCode = AppFunctionException.ERROR_DENIED; } else if (e instanceof DisabledAppFunctionException) { - resultCode = ExecuteAppFunctionResponse.RESULT_DISABLED; + resultCode = AppFunctionException.ERROR_DISABLED; } - return ExecuteAppFunctionResponse.newFailure( - resultCode, e.getMessage(), /* extras= */ null); + return new AppFunctionException(resultCode, e.getMessage()); } private int mapAppSearchResultFailureCodeToExecuteAppFunctionResponse(int resultCode) { @@ -485,13 +477,13 @@ public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub { switch (resultCode) { case AppSearchResult.RESULT_NOT_FOUND: - return ExecuteAppFunctionResponse.RESULT_FUNCTION_NOT_FOUND; + return AppFunctionException.ERROR_FUNCTION_NOT_FOUND; case AppSearchResult.RESULT_INVALID_ARGUMENT: case AppSearchResult.RESULT_INTERNAL_ERROR: case AppSearchResult.RESULT_SECURITY_ERROR: // fall-through } - return ExecuteAppFunctionResponse.RESULT_SYSTEM_ERROR; + return AppFunctionException.ERROR_SYSTEM_ERROR; } private void registerAppSearchObserver(@NonNull TargetUser user) { diff --git a/services/appfunctions/java/com/android/server/appfunctions/RunAppFunctionServiceCallback.java b/services/appfunctions/java/com/android/server/appfunctions/RunAppFunctionServiceCallback.java index 129be65f3153..c689bb92f8f7 100644 --- a/services/appfunctions/java/com/android/server/appfunctions/RunAppFunctionServiceCallback.java +++ b/services/appfunctions/java/com/android/server/appfunctions/RunAppFunctionServiceCallback.java @@ -17,6 +17,7 @@ package com.android.server.appfunctions; import android.annotation.NonNull; import android.app.appfunctions.ExecuteAppFunctionAidlRequest; +import android.app.appfunctions.AppFunctionException; import android.app.appfunctions.ExecuteAppFunctionResponse; import android.app.appfunctions.IAppFunctionService; import android.app.appfunctions.ICancellationCallback; @@ -57,17 +58,22 @@ public class RunAppFunctionServiceCallback implements RunServiceCallCallback<IAp mCancellationCallback, new IExecuteAppFunctionCallback.Stub() { @Override - public void onResult(ExecuteAppFunctionResponse response) { + public void onSuccess(ExecuteAppFunctionResponse response) { mSafeExecuteAppFunctionCallback.onResult(response); serviceUsageCompleteListener.onCompleted(); } + + @Override + public void onError(AppFunctionException error) { + mSafeExecuteAppFunctionCallback.onError(error); + serviceUsageCompleteListener.onCompleted(); + } }); } catch (Exception e) { - mSafeExecuteAppFunctionCallback.onResult( - ExecuteAppFunctionResponse.newFailure( - ExecuteAppFunctionResponse.RESULT_APP_UNKNOWN_ERROR, - e.getMessage(), - /* extras= */ null)); + mSafeExecuteAppFunctionCallback.onError( + new AppFunctionException( + AppFunctionException.ERROR_APP_UNKNOWN_ERROR, + e.getMessage())); serviceUsageCompleteListener.onCompleted(); } } @@ -75,11 +81,9 @@ public class RunAppFunctionServiceCallback implements RunServiceCallCallback<IAp @Override public void onFailedToConnect() { Slog.e(TAG, "Failed to connect to service"); - mSafeExecuteAppFunctionCallback.onResult( - ExecuteAppFunctionResponse.newFailure( - ExecuteAppFunctionResponse.RESULT_APP_UNKNOWN_ERROR, - "Failed to connect to AppFunctionService", - /* extras= */ null)); + mSafeExecuteAppFunctionCallback.onError( + new AppFunctionException(AppFunctionException.ERROR_APP_UNKNOWN_ERROR, + "Failed to connect to AppFunctionService")); } @Override diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java index 8567ccb9e7a5..b221d74e2d86 100644 --- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java +++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java @@ -187,11 +187,6 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku // Simple flag to enable/disable debug logging. private static final boolean DEBUG = Build.IS_DEBUGGABLE; - // String constants for XML schema migration related to changes in keyguard package. - private static final String OLD_KEYGUARD_HOST_PACKAGE = "android"; - private static final String NEW_KEYGUARD_HOST_PACKAGE = "com.android.keyguard"; - private static final int KEYGUARD_HOST_ID = 0x4b455947; - // Filename for app widgets state persisted on disk. private static final String STATE_FILENAME = "appwidgets.xml"; @@ -211,6 +206,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku private static final int UNKNOWN_USER_ID = -10; // Version of XML schema for app widgets. Bump if the stored widgets need to be upgraded. + // Version 1 introduced in 2014 - Android 5.0 private static final int CURRENT_VERSION = 1; // Every widget update request is associated which an increasing sequence number. This is @@ -4428,19 +4424,11 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku int version = fromVersion; - // Update 1: keyguard moved from package "android" to "com.android.keyguard" + // Update 1: From version 0 to 1, was used from Android 4 to Android 5. It updated the + // location of the keyguard widget database. No modern device will have db version 0. if (version == 0) { - HostId oldHostId = new HostId(Process.myUid(), - KEYGUARD_HOST_ID, OLD_KEYGUARD_HOST_PACKAGE); - - Host host = lookupHostLocked(oldHostId); - if (host != null) { - final int uid = getUidForPackage(NEW_KEYGUARD_HOST_PACKAGE, - UserHandle.USER_SYSTEM); - if (uid >= 0) { - host.id = new HostId(uid, KEYGUARD_HOST_ID, NEW_KEYGUARD_HOST_PACKAGE); - } - } + Slog.e(TAG, "Found widget database with version 0, this should not be possible," + + " forcing upgrade to version 1"); version = 1; } @@ -4450,24 +4438,8 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku } } - private static File getStateFile(int userId) { - return new File(Environment.getUserSystemDirectory(userId), STATE_FILENAME); - } - private static AtomicFile getSavedStateFile(int userId) { - File dir = Environment.getUserSystemDirectory(userId); - File settingsFile = getStateFile(userId); - if (!settingsFile.exists() && userId == UserHandle.USER_SYSTEM) { - if (!dir.exists()) { - dir.mkdirs(); - } - // Migrate old data - File oldFile = new File("/data/system/" + STATE_FILENAME); - // Method doesn't throw an exception on failure. Ignore any errors - // in moving the file (like non-existence) - oldFile.renameTo(settingsFile); - } - return new AtomicFile(settingsFile); + return new AtomicFile(new File(Environment.getUserSystemDirectory(userId), STATE_FILENAME)); } void onUserStopped(int userId) { diff --git a/services/autofill/features.aconfig b/services/autofill/features.aconfig index 1dc3b73d2bd3..bd46debf12ca 100644 --- a/services/autofill/features.aconfig +++ b/services/autofill/features.aconfig @@ -22,3 +22,11 @@ flag { description: "Guards against Autofill-Credman Phase1 developer integration via new APIs" bug: "320730001" } + +flag { + name: "fill_dialog_improvements" + is_exported: true + namespace: "autofill" + description: "Improvements for Fill Dialog, including deprecation of pre-trigger API's" + bug: "336223371" +} diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java index ddccb3731cc1..466d477992b3 100644 --- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java +++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java @@ -281,6 +281,9 @@ public class UserBackupManagerService { private static final int SCHEDULE_FILE_VERSION = 1; public static final String SETTINGS_PACKAGE = "com.android.providers.settings"; + + public static final String TELEPHONY_PROVIDER_PACKAGE = "com.android.providers.telephony"; + public static final String SHARED_BACKUP_AGENT_PACKAGE = "com.android.sharedstoragebackup"; // Pseudoname that we use for the Package Manager metadata "package". diff --git a/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java b/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java index f24a3c1afc86..508b62cd83f0 100644 --- a/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java +++ b/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java @@ -21,6 +21,7 @@ import static com.android.server.backup.BackupManagerService.TAG; import static com.android.server.backup.UserBackupManagerService.PACKAGE_MANAGER_SENTINEL; import static com.android.server.backup.UserBackupManagerService.SETTINGS_PACKAGE; import static com.android.server.backup.UserBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE; +import static com.android.server.backup.UserBackupManagerService.TELEPHONY_PROVIDER_PACKAGE; import static com.android.server.backup.UserBackupManagerService.WALLPAPER_PACKAGE; import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME; @@ -75,6 +76,12 @@ public class BackupEligibilityRules { systemPackagesAllowedForProfileUser, Sets.newArraySet(WALLPAPER_PACKAGE, SETTINGS_PACKAGE)); + static { + if (UserManager.isHeadlessSystemUserMode()) { + systemPackagesAllowedForNonSystemUsers.add(TELEPHONY_PROVIDER_PACKAGE); + } + } + private final PackageManager mPackageManager; private final PackageManagerInternal mPackageManagerInternal; private final int mUserId; diff --git a/services/core/Android.bp b/services/core/Android.bp index 3ccad16073a7..aea16b08df49 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -158,6 +158,7 @@ java_library_static { "android.hardware.gnss-V2-java", "android.hardware.vibrator-V3-java", "app-compat-annotations", + "art_exported_aconfig_flags_lib", "framework-tethering.stubs.module_lib", "keepanno-annotations", "service-art.stubs.system_server", @@ -237,6 +238,7 @@ java_library_static { "connectivity_flags_lib", "device_config_service_flags_java", "dreams_flags_lib", + "aconfig_flags_java", "aconfig_new_storage_flags_lib", "powerstats_flags_lib", "locksettings_flags_lib", diff --git a/services/core/java/com/android/server/BootReceiver.java b/services/core/java/com/android/server/BootReceiver.java index 23cee9db2138..1588e0421675 100644 --- a/services/core/java/com/android/server/BootReceiver.java +++ b/services/core/java/com/android/server/BootReceiver.java @@ -53,6 +53,7 @@ import com.android.server.am.DropboxRateLimiter; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; +import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; @@ -400,9 +401,18 @@ public class BootReceiver extends BroadcastReceiver { Slog.w(TAG, "Tombstone too large to add to DropBox: " + tombstone.toPath()); return; } - // Read the proto tombstone file as bytes. - final byte[] tombstoneBytes = Files.readAllBytes(tombstone.toPath()); + // Read the proto tombstone file as bytes. + // Previously used Files.readAllBytes() which internally creates a ThreadLocal BufferCache + // via ChannelInputStream that isn't properly released. Switched to + // FileInputStream.transferTo() which avoids the NIO channels completely, + // preventing the memory leak while maintaining the same functionality. + final byte[] tombstoneBytes; + try (FileInputStream fis = new FileInputStream(tombstone); + ByteArrayOutputStream baos = new ByteArrayOutputStream()) { + fis.transferTo(baos); + tombstoneBytes = baos.toByteArray(); + } final File tombstoneProtoWithHeaders = File.createTempFile( tombstone.getName(), ".tmp", TOMBSTONE_TMP_DIR); Files.setPosixFilePermissions( diff --git a/services/core/java/com/android/server/ConsumerIrService.java b/services/core/java/com/android/server/ConsumerIrService.java index ee6d808aa549..8362079b9009 100644 --- a/services/core/java/com/android/server/ConsumerIrService.java +++ b/services/core/java/com/android/server/ConsumerIrService.java @@ -30,6 +30,8 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.util.Slog; +import com.android.server.utils.LazyJniRegistrar; + public class ConsumerIrService extends IConsumerIrService.Stub { private static final String TAG = "ConsumerIrService"; @@ -39,6 +41,10 @@ public class ConsumerIrService extends IConsumerIrService.Stub { private static native int halTransmit(int carrierFrequency, int[] pattern); private static native int[] halGetCarrierFrequencies(); + static { + LazyJniRegistrar.registerConsumerIrService(); + } + private final Context mContext; private final PowerManager.WakeLock mWakeLock; private final boolean mHasNativeHal; diff --git a/services/core/java/com/android/server/SystemConfig.java b/services/core/java/com/android/server/SystemConfig.java index 1082e6214935..99772c39bef1 100644 --- a/services/core/java/com/android/server/SystemConfig.java +++ b/services/core/java/com/android/server/SystemConfig.java @@ -47,6 +47,7 @@ import android.util.Xml; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.pm.RoSystemFeatures; +import com.android.internal.pm.pkg.parsing.ParsingPackageUtils; import com.android.internal.util.XmlUtils; import com.android.modules.utils.build.UnboundedSdkLevel; import com.android.server.pm.permission.PermissionAllowlist; @@ -2000,6 +2001,13 @@ public class SystemConfig { private void readSplitPermission(XmlPullParser parser, File permFile) throws IOException, XmlPullParserException { + // If trunkstable feature flag disabled for this split permission, skip this tag. + if (ParsingPackageUtils.getAconfigFlags() + .skipCurrentElement(/* pkg= */ null, parser, /* allowNoNamespace= */ true)) { + XmlUtils.skipCurrentTag(parser); + return; + } + String splitPerm = parser.getAttributeValue(null, "name"); if (splitPerm == null) { Slog.w(TAG, "<split-permission> without name in " + permFile + " at " diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index 72a9a2d6de26..fa228627c255 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -88,6 +88,7 @@ import android.telephony.emergency.EmergencyNumber; import android.telephony.ims.ImsCallSession; import android.telephony.ims.ImsReasonInfo; import android.telephony.ims.MediaQualityStatus; +import android.telephony.satellite.NtnSignalStrength; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; @@ -440,6 +441,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { private boolean[] mCarrierRoamingNtnEligible = null; private List<IntArray> mCarrierRoamingNtnAvailableServices; + private NtnSignalStrength[] mCarrierRoamingNtnSignalStrength; // Local cache to check if Satellite Modem is enabled private AtomicBoolean mIsSatelliteEnabled; @@ -745,6 +747,12 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mSCBMDuration = copyOf(mSCBMDuration, mNumPhones); mCarrierRoamingNtnMode = copyOf(mCarrierRoamingNtnMode, mNumPhones); mCarrierRoamingNtnEligible = copyOf(mCarrierRoamingNtnEligible, mNumPhones); + if (mCarrierRoamingNtnSignalStrength != null) { + mCarrierRoamingNtnSignalStrength = copyOf( + mCarrierRoamingNtnSignalStrength, mNumPhones); + } else { + mCarrierRoamingNtnSignalStrength = new NtnSignalStrength[mNumPhones]; + } // ds -> ss switch. if (mNumPhones < oldNumPhones) { cutListToSize(mCellInfo, mNumPhones); @@ -807,6 +815,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mCarrierRoamingNtnMode[i] = false; mCarrierRoamingNtnEligible[i] = false; mCarrierRoamingNtnAvailableServices.add(i, new IntArray()); + mCarrierRoamingNtnSignalStrength[i] = new NtnSignalStrength( + NtnSignalStrength.NTN_SIGNAL_STRENGTH_NONE); } } } @@ -883,6 +893,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mCarrierRoamingNtnMode = new boolean[numPhones]; mCarrierRoamingNtnEligible = new boolean[numPhones]; mCarrierRoamingNtnAvailableServices = new ArrayList<>(); + mCarrierRoamingNtnSignalStrength = new NtnSignalStrength[numPhones]; mIsSatelliteEnabled = new AtomicBoolean(); mWasSatelliteEnabledNotified = new AtomicBoolean(); @@ -932,6 +943,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mCarrierRoamingNtnMode[i] = false; mCarrierRoamingNtnEligible[i] = false; mCarrierRoamingNtnAvailableServices.add(i, new IntArray()); + mCarrierRoamingNtnSignalStrength[i] = new NtnSignalStrength( + NtnSignalStrength.NTN_SIGNAL_STRENGTH_NONE); } mAppOps = mContext.getSystemService(AppOpsManager.class); @@ -1565,6 +1578,15 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { remove(r.binder); } } + if (events.contains( + TelephonyCallback.EVENT_CARRIER_ROAMING_NTN_SIGNAL_STRENGTH_CHANGED)) { + try { + r.callback.onCarrierRoamingNtnSignalStrengthChanged( + mCarrierRoamingNtnSignalStrength[r.phoneId]); + } catch (RemoteException ex) { + remove(r.binder); + } + } } } } @@ -3803,6 +3825,44 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } + + /** + * Notify external listeners that carrier roaming non-terrestrial network + * signal strength changed. + * @param subId subscription ID. + * @param ntnSignalStrength non-terrestrial network signal strength. + */ + public void notifyCarrierRoamingNtnSignalStrengthChanged(int subId, + @NonNull NtnSignalStrength ntnSignalStrength) { + if (!checkNotifyPermission("notifyCarrierRoamingNtnSignalStrengthChanged")) { + log("nnotifyCarrierRoamingNtnSignalStrengthChanged: caller does not have required " + + "permissions."); + return; + } + + if (VDBG) { + log("notifyCarrierRoamingNtnSignalStrengthChanged: " + + "subId=" + subId + " ntnSignalStrength=" + ntnSignalStrength.getLevel()); + } + + synchronized (mRecords) { + int phoneId = getPhoneIdFromSubId(subId); + mCarrierRoamingNtnSignalStrength[phoneId] = ntnSignalStrength; + for (Record r : mRecords) { + if (r.matchTelephonyCallbackEvent( + TelephonyCallback.EVENT_CARRIER_ROAMING_NTN_SIGNAL_STRENGTH_CHANGED) + && idMatch(r, subId, phoneId)) { + try { + r.callback.onCarrierRoamingNtnSignalStrengthChanged(ntnSignalStrength); + } catch (RemoteException ex) { + mRemoveList.add(r.binder); + } + } + } + handleRemoveListLocked(); + } + } + @NeverCompile // Avoid size overhead of debugging code. @Override public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { @@ -3858,6 +3918,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { pw.println("mSCBMDuration=" + mSCBMDuration[i]); pw.println("mCarrierRoamingNtnMode=" + mCarrierRoamingNtnMode[i]); pw.println("mCarrierRoamingNtnEligible=" + mCarrierRoamingNtnEligible[i]); + pw.println("mCarrierRoamingNtnSignalStrength=" + + mCarrierRoamingNtnSignalStrength[i]); // We need to obfuscate package names, and primitive arrays' native toString is ugly Pair<List<String>, int[]> carrierPrivilegeState = mCarrierPrivilegeStates.get(i); diff --git a/services/core/java/com/android/server/adaptiveauth/OWNERS b/services/core/java/com/android/server/adaptiveauth/OWNERS deleted file mode 100644 index b18810564d88..000000000000 --- a/services/core/java/com/android/server/adaptiveauth/OWNERS +++ /dev/null @@ -1 +0,0 @@ -hainingc@google.com
\ No newline at end of file diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 0826c5333e2f..dfddc089e4a4 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -668,6 +668,8 @@ public class ActivityManagerService extends IActivityManager.Stub */ private static final boolean ENABLE_PROC_LOCK = true; + private static final int DEFAULT_INTENT_CREATOR_UID = -1; + /** * The lock for process management. * @@ -19307,36 +19309,37 @@ public class ActivityManagerService extends IActivityManager.Stub public void addCreatorToken(@Nullable Intent intent, String creatorPackage) { if (!preventIntentRedirect()) return; - if (intent == null || intent.getExtraIntentKeys() == null) return; - for (String key : intent.getExtraIntentKeys()) { - try { - Intent extraIntent = intent.getParcelableExtra(key, Intent.class); - if (extraIntent == null) { - Slog.w(TAG, "The key {" + key - + "} does not correspond to an intent in the extra bundle."); - continue; - } - IntentCreatorToken creatorToken = createIntentCreatorToken(extraIntent, - creatorPackage); - if (creatorToken != null) { - extraIntent.setCreatorToken(creatorToken); - Slog.wtf(TAG, "A creator token is added to an intent. creatorPackage: " - + creatorPackage + "; intent: " + intent); - FrameworkStatsLog.write(INTENT_CREATOR_TOKEN_ADDED, - creatorToken.getCreatorUid()); - } - } catch (Exception e) { - Slog.wtf(TAG, - "Something went wrong when trying to add creator token for embedded " - + "intents of intent: ." - + intent, e); + if (intent == null) return; + + String targetPackage = intent.getComponent() != null + ? intent.getComponent().getPackageName() + : intent.getPackage(); + final boolean isCreatorSameAsTarget = creatorPackage != null && creatorPackage.equals( + targetPackage); + final boolean noExtraIntentKeys = + intent.getExtraIntentKeys() == null || intent.getExtraIntentKeys().isEmpty(); + final int creatorUid = noExtraIntentKeys ? DEFAULT_INTENT_CREATOR_UID : Binder.getCallingUid(); + + intent.forEachNestedCreatorToken(extraIntent -> { + if (isCreatorSameAsTarget) { + FrameworkStatsLog.write(INTENT_CREATOR_TOKEN_ADDED, creatorUid, true); + return; } - } + IntentCreatorToken creatorToken = createIntentCreatorToken(extraIntent, creatorUid, + creatorPackage); + if (creatorToken != null) { + extraIntent.setCreatorToken(creatorToken); + // TODO remove Slog.wtf once proven FrameworkStatsLog works. b/375396329 + Slog.wtf(TAG, "A creator token is added to an intent. creatorPackage: " + + creatorPackage + "; intent: " + extraIntent); + FrameworkStatsLog.write(INTENT_CREATOR_TOKEN_ADDED, creatorUid, false); + } + }); } - private IntentCreatorToken createIntentCreatorToken(Intent intent, String creatorPackage) { + private IntentCreatorToken createIntentCreatorToken(Intent intent, int creatorUid, + String creatorPackage) { if (IntentCreatorToken.isValid(intent)) return null; - int creatorUid = getCallingUid(); IntentCreatorToken.Key key = new IntentCreatorToken.Key(creatorUid, creatorPackage, intent); IntentCreatorToken token; synchronized (sIntentCreatorTokenCache) { diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index aa9ac6c36784..2eb9f3cb600f 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -124,6 +124,7 @@ import com.android.server.LocalServices; import com.android.server.Watchdog; import com.android.server.net.BaseNetworkObserver; import com.android.server.pm.UserManagerInternal; +import com.android.server.power.feature.PowerManagerFlags; import com.android.server.power.optimization.Flags; import com.android.server.power.stats.BatteryExternalStatsWorker; import com.android.server.power.stats.BatteryStatsDumpHelperImpl; @@ -195,6 +196,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub private final BatteryStats.BatteryStatsDumpHelper mDumpHelper; private final PowerStatsUidResolver mPowerStatsUidResolver = new PowerStatsUidResolver(); private final PowerAttributor mPowerAttributor; + private final PowerManagerFlags mPowerManagerFlags = new PowerManagerFlags(); private volatile boolean mMonitorEnabled = true; private boolean mRailsStatsCollectionEnabled = true; @@ -617,6 +619,9 @@ public final class BatteryStatsService extends IBatteryStats.Stub BatteryConsumer.POWER_COMPONENT_ANY, Flags.streamlinedMiscBatteryStats()); + mStats.setMoveWscLoggingToNotifierEnabled( + mPowerManagerFlags.isMoveWscLoggingToNotifierEnabled()); + mWorker.systemServicesReady(); mStats.systemServicesReady(mContext); mCpuWakeupStats.systemServicesReady(); diff --git a/services/core/java/com/android/server/am/BroadcastController.java b/services/core/java/com/android/server/am/BroadcastController.java index b0f880710eb6..8a128582c507 100644 --- a/services/core/java/com/android/server/am/BroadcastController.java +++ b/services/core/java/com/android/server/am/BroadcastController.java @@ -593,7 +593,7 @@ class BroadcastController { originalStickyCallingUid, BackgroundStartPrivileges.NONE, false /* only PRE_BOOT_COMPLETED should be exempt, no stickies */, null /* filterExtrasForReceiver */, - broadcast.originalCallingAppProcessState); + broadcast.originalCallingAppProcessState, mService.mPlatformCompat); queue.enqueueBroadcastLocked(r); } } @@ -1631,7 +1631,7 @@ class BroadcastController { receivers, resultToApp, resultTo, resultCode, resultData, resultExtras, ordered, sticky, false, userId, backgroundStartPrivileges, timeoutExempt, filterExtrasForReceiver, - callerAppProcessState); + callerAppProcessState, mService.mPlatformCompat); broadcastSentEventRecord.setBroadcastRecord(r); if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing ordered broadcast " + r); diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java index f908c67d7ec9..38df10a0bc8c 100644 --- a/services/core/java/com/android/server/am/BroadcastRecord.java +++ b/services/core/java/com/android/server/am/BroadcastRecord.java @@ -42,6 +42,8 @@ import android.app.AppOpsManager; import android.app.BackgroundStartPrivileges; import android.app.BroadcastOptions; import android.app.BroadcastOptions.DeliveryGroupPolicy; +import android.compat.annotation.ChangeId; +import android.compat.annotation.EnabledSince; import android.content.ComponentName; import android.content.IIntentReceiver; import android.content.Intent; @@ -55,10 +57,12 @@ import android.os.UserHandle; import android.util.ArrayMap; import android.util.IntArray; import android.util.PrintWriterPrinter; +import android.util.SparseBooleanArray; import android.util.TimeUtils; import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.VisibleForTesting; +import com.android.server.compat.PlatformCompat; import dalvik.annotation.optimization.NeverCompile; @@ -77,6 +81,15 @@ import java.util.function.BiFunction; * An active intent broadcast. */ final class BroadcastRecord extends Binder { + /** + * Limit the scope of the priority values to the process level. This means that priority values + * will only influence the order of broadcast delivery within the same process. + */ + @ChangeId + @EnabledSince(targetSdkVersion = android.os.Build.VERSION_CODES.BASE) + @VisibleForTesting + static final long CHANGE_LIMIT_PRIORITY_SCOPE = 371307720L; + final @NonNull Intent intent; // the original intent that generated us final @Nullable ComponentName targetComp; // original component name set on the intent final @Nullable ProcessRecord callerApp; // process that sent this @@ -417,13 +430,13 @@ final class BroadcastRecord extends Binder { @NonNull BackgroundStartPrivileges backgroundStartPrivileges, boolean timeoutExempt, @Nullable BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver, - int callerAppProcessState) { + int callerAppProcessState, PlatformCompat platformCompat) { this(queue, intent, callerApp, callerPackage, callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType, requiredPermissions, excludedPermissions, excludedPackages, appOp, options, receivers, resultToApp, resultTo, resultCode, resultData, resultExtras, serialized, sticky, initialSticky, userId, -1, backgroundStartPrivileges, timeoutExempt, - filterExtrasForReceiver, callerAppProcessState); + filterExtrasForReceiver, callerAppProcessState, platformCompat); } BroadcastRecord(BroadcastQueue _queue, @@ -439,7 +452,7 @@ final class BroadcastRecord extends Binder { @NonNull BackgroundStartPrivileges backgroundStartPrivileges, boolean timeoutExempt, @Nullable BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver, - int callerAppProcessState) { + int callerAppProcessState, PlatformCompat platformCompat) { if (_intent == null) { throw new NullPointerException("Can't construct with a null intent"); } @@ -466,7 +479,8 @@ final class BroadcastRecord extends Binder { urgent = calculateUrgent(_intent, _options); deferUntilActive = calculateDeferUntilActive(_callingUid, _options, _resultTo, _serialized, urgent); - blockedUntilBeyondCount = calculateBlockedUntilBeyondCount(receivers, _serialized); + blockedUntilBeyondCount = calculateBlockedUntilBeyondCount( + receivers, _serialized, platformCompat); scheduledTime = new long[delivery.length]; terminalTime = new long[delivery.length]; resultToApp = _resultToApp; @@ -730,7 +744,8 @@ final class BroadcastRecord extends Binder { } /** - * Determine if the result of {@link #calculateBlockedUntilBeyondCount(List, boolean)} + * Determine if the result of + * {@link #calculateBlockedUntilBeyondCount(List, boolean, PlatformCompat)} * has prioritized tranches of receivers. */ @VisibleForTesting @@ -754,37 +769,121 @@ final class BroadcastRecord extends Binder { */ @VisibleForTesting static @NonNull int[] calculateBlockedUntilBeyondCount( - @NonNull List<Object> receivers, boolean ordered) { + @NonNull List<Object> receivers, boolean ordered, PlatformCompat platformCompat) { final int N = receivers.size(); final int[] blockedUntilBeyondCount = new int[N]; - int lastPriority = 0; - int lastPriorityIndex = 0; - for (int i = 0; i < N; i++) { - if (ordered) { - // When sending an ordered broadcast, we need to block this - // receiver until all previous receivers have terminated + if (ordered) { + // When sending an ordered broadcast, we need to block this + // receiver until all previous receivers have terminated + for (int i = 0; i < N; i++) { blockedUntilBeyondCount[i] = i; + } + } else { + if (Flags.limitPriorityScope()) { + final boolean[] changeEnabled = calculateChangeStateForReceivers( + receivers, CHANGE_LIMIT_PRIORITY_SCOPE, platformCompat); + + // Priority of the previous tranche + int lastTranchePriority = 0; + // Priority of the current tranche + int currentTranchePriority = 0; + // Index of the last receiver in the previous tranche + int lastTranchePriorityIndex = -1; + // Index of the last receiver with change disabled in the previous tranche + int lastTrancheChangeDisabledIndex = -1; + // Index of the last receiver with change disabled in the current tranche + int currentTrancheChangeDisabledIndex = -1; + + for (int i = 0; i < N; i++) { + final int thisPriority = getReceiverPriority(receivers.get(i)); + if (i == 0) { + currentTranchePriority = thisPriority; + if (!changeEnabled[i]) { + currentTrancheChangeDisabledIndex = i; + } + continue; + } + + // Check if a new priority tranche has started + if (thisPriority != currentTranchePriority) { + // Update tranche boundaries and reset the disabled index. + if (currentTrancheChangeDisabledIndex != -1) { + lastTrancheChangeDisabledIndex = currentTrancheChangeDisabledIndex; + } + lastTranchePriority = currentTranchePriority; + lastTranchePriorityIndex = i - 1; + currentTranchePriority = thisPriority; + currentTrancheChangeDisabledIndex = -1; + } + if (!changeEnabled[i]) { + currentTrancheChangeDisabledIndex = i; + + // Since the change is disabled, block the current receiver until the + // last receiver in the previous tranche. + blockedUntilBeyondCount[i] = lastTranchePriorityIndex + 1; + } else if (thisPriority != lastTranchePriority) { + // If the changeId was disabled for an earlier receiver and the current + // receiver has a different priority, block the current receiver + // until that earlier receiver. + if (lastTrancheChangeDisabledIndex != -1) { + blockedUntilBeyondCount[i] = lastTrancheChangeDisabledIndex + 1; + } + } + } + // If the entire list is in the same priority tranche or no receivers had + // changeId disabled, mark as -1 to indicate that none of them need to wait + if (N > 0 && (lastTranchePriorityIndex == -1 + || (lastTrancheChangeDisabledIndex == -1 + && currentTrancheChangeDisabledIndex == -1))) { + Arrays.fill(blockedUntilBeyondCount, -1); + } } else { // When sending a prioritized broadcast, we only need to wait // for the previous tranche of receivers to be terminated - final int thisPriority = getReceiverPriority(receivers.get(i)); - if ((i == 0) || (thisPriority != lastPriority)) { - lastPriority = thisPriority; - lastPriorityIndex = i; - blockedUntilBeyondCount[i] = i; - } else { - blockedUntilBeyondCount[i] = lastPriorityIndex; + int lastPriority = 0; + int lastPriorityIndex = 0; + for (int i = 0; i < N; i++) { + final int thisPriority = getReceiverPriority(receivers.get(i)); + if ((i == 0) || (thisPriority != lastPriority)) { + lastPriority = thisPriority; + lastPriorityIndex = i; + blockedUntilBeyondCount[i] = i; + } else { + blockedUntilBeyondCount[i] = lastPriorityIndex; + } + } + // If the entire list is in the same priority tranche, mark as -1 to + // indicate that none of them need to wait + if (N > 0 && blockedUntilBeyondCount[N - 1] == 0) { + Arrays.fill(blockedUntilBeyondCount, -1); } } } - // If the entire list is in the same priority tranche, mark as -1 to - // indicate that none of them need to wait - if (N > 0 && blockedUntilBeyondCount[N - 1] == 0) { - Arrays.fill(blockedUntilBeyondCount, -1); - } return blockedUntilBeyondCount; } + @VisibleForTesting + static @NonNull boolean[] calculateChangeStateForReceivers(@NonNull List<Object> receivers, + long changeId, PlatformCompat platformCompat) { + final SparseBooleanArray changeStateForUids = new SparseBooleanArray(); + final int count = receivers.size(); + final boolean[] changeStateForReceivers = new boolean[count]; + for (int i = 0; i < count; ++i) { + final int receiverUid = getReceiverUid(receivers.get(i)); + final boolean isChangeEnabled; + final int idx = changeStateForUids.indexOfKey(receiverUid); + if (idx >= 0) { + isChangeEnabled = changeStateForUids.valueAt(idx); + } else { + isChangeEnabled = platformCompat.isChangeEnabledByUidInternalNoLogging( + changeId, receiverUid); + changeStateForUids.put(receiverUid, isChangeEnabled); + } + changeStateForReceivers[i] = isChangeEnabled; + } + return changeStateForReceivers; + } + static int getReceiverUid(@NonNull Object receiver) { if (receiver instanceof BroadcastFilter) { return ((BroadcastFilter) receiver).owningUid; diff --git a/services/core/java/com/android/server/am/BroadcastSkipPolicy.java b/services/core/java/com/android/server/am/BroadcastSkipPolicy.java index f4a931f89551..d2af84cf3d30 100644 --- a/services/core/java/com/android/server/am/BroadcastSkipPolicy.java +++ b/services/core/java/com/android/server/am/BroadcastSkipPolicy.java @@ -19,7 +19,6 @@ package com.android.server.am; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW; import static com.android.server.am.ActivityManagerService.checkComponentPermission; import static com.android.server.am.BroadcastQueue.TAG; -import static com.android.server.am.Flags.usePermissionManagerForBroadcastDeliveryCheck; import android.annotation.NonNull; import android.annotation.Nullable; @@ -289,33 +288,16 @@ public class BroadcastSkipPolicy { if (info.activityInfo.applicationInfo.uid != Process.SYSTEM_UID && r.requiredPermissions != null && r.requiredPermissions.length > 0) { - final AttributionSource[] attributionSources; - if (usePermissionManagerForBroadcastDeliveryCheck()) { - attributionSources = createAttributionSourcesForResolveInfo(info); - } else { - attributionSources = null; - } + final AttributionSource[] attributionSources = + createAttributionSourcesForResolveInfo(info); for (int i = 0; i < r.requiredPermissions.length; i++) { String requiredPermission = r.requiredPermissions[i]; - try { - if (usePermissionManagerForBroadcastDeliveryCheck()) { - perm = hasPermissionForDataDelivery( - requiredPermission, - "Broadcast delivered to " + info.activityInfo.name, - attributionSources) - ? PackageManager.PERMISSION_GRANTED - : PackageManager.PERMISSION_DENIED; - } else { - perm = AppGlobals.getPackageManager() - .checkPermission( - requiredPermission, - info.activityInfo.applicationInfo.packageName, - UserHandle - .getUserId(info.activityInfo.applicationInfo.uid)); - } - } catch (RemoteException e) { - perm = PackageManager.PERMISSION_DENIED; - } + perm = hasPermissionForDataDelivery( + requiredPermission, + "Broadcast delivered to " + info.activityInfo.name, + attributionSources) + ? PackageManager.PERMISSION_GRANTED + : PackageManager.PERMISSION_DENIED; if (perm != PackageManager.PERMISSION_GRANTED) { return "Permission Denial: receiving " + r.intent + " to " @@ -324,15 +306,6 @@ public class BroadcastSkipPolicy { + " due to sender " + r.callerPackage + " (uid " + r.callingUid + ")"; } - if (!usePermissionManagerForBroadcastDeliveryCheck()) { - int appOp = AppOpsManager.permissionToOpCode(requiredPermission); - if (appOp != AppOpsManager.OP_NONE && appOp != r.appOp) { - if (!noteOpForManifestReceiver(appOp, r, info, component)) { - return "Skipping delivery to " + info.activityInfo.packageName - + " due to required appop " + appOp; - } - } - } } } if (r.appOp != AppOpsManager.OP_NONE) { @@ -452,35 +425,20 @@ public class BroadcastSkipPolicy { // Check that the receiver has the required permission(s) to receive this broadcast. if (r.requiredPermissions != null && r.requiredPermissions.length > 0) { - final AttributionSource attributionSource; - if (usePermissionManagerForBroadcastDeliveryCheck()) { - attributionSource = - new AttributionSource.Builder(filter.receiverList.uid) - .setPid(filter.receiverList.pid) - .setPackageName(filter.packageName) - .setAttributionTag(filter.featureId) - .build(); - } else { - attributionSource = null; - } + final AttributionSource attributionSource = + new AttributionSource.Builder(filter.receiverList.uid) + .setPid(filter.receiverList.pid) + .setPackageName(filter.packageName) + .setAttributionTag(filter.featureId) + .build(); for (int i = 0; i < r.requiredPermissions.length; i++) { String requiredPermission = r.requiredPermissions[i]; - final int perm; - if (usePermissionManagerForBroadcastDeliveryCheck()) { - perm = hasPermissionForDataDelivery( - requiredPermission, - "Broadcast delivered to registered receiver " + filter.receiverId, - attributionSource) - ? PackageManager.PERMISSION_GRANTED - : PackageManager.PERMISSION_DENIED; - } else { - perm = checkComponentPermission( - requiredPermission, - filter.receiverList.pid, - filter.receiverList.uid, - -1 /* owningUid */, - true /* exported */); - } + final int perm = hasPermissionForDataDelivery( + requiredPermission, + "Broadcast delivered to registered receiver " + filter.receiverId, + attributionSource) + ? PackageManager.PERMISSION_GRANTED + : PackageManager.PERMISSION_DENIED; if (perm != PackageManager.PERMISSION_GRANTED) { return "Permission Denial: receiving " + r.intent.toString() @@ -491,24 +449,6 @@ public class BroadcastSkipPolicy { + " due to sender " + r.callerPackage + " (uid " + r.callingUid + ")"; } - if (!usePermissionManagerForBroadcastDeliveryCheck()) { - int appOp = AppOpsManager.permissionToOpCode(requiredPermission); - if (appOp != AppOpsManager.OP_NONE && appOp != r.appOp - && mService.getAppOpsManager().noteOpNoThrow(appOp, - filter.receiverList.uid, filter.packageName, filter.featureId, - "Broadcast delivered to registered receiver " + filter.receiverId) - != AppOpsManager.MODE_ALLOWED) { - return "Appop Denial: receiving " - + r.intent.toString() - + " to " + filter.receiverList.app - + " (pid=" + filter.receiverList.pid - + ", uid=" + filter.receiverList.uid + ")" - + " requires appop " + AppOpsManager.permissionToOp( - requiredPermission) - + " due to sender " + r.callerPackage - + " (uid " + r.callingUid + ")"; - } - } } } if ((r.requiredPermissions == null || r.requiredPermissions.length == 0)) { diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java index 416c11090515..da5b1fd1c079 100644 --- a/services/core/java/com/android/server/am/CachedAppOptimizer.java +++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java @@ -101,7 +101,7 @@ import java.util.Random; import java.util.Set; import java.util.concurrent.Executor; -public final class CachedAppOptimizer { +public class CachedAppOptimizer { // Flags stored in the DeviceConfig API. @VisibleForTesting static final String KEY_USE_COMPACTION = "use_compaction"; diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index c0676623a1e9..b84bf6b90711 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -19,6 +19,7 @@ package com.android.server.am; import static android.app.ActivityManager.PROCESS_CAPABILITY_ALL; import static android.app.ActivityManager.PROCESS_CAPABILITY_ALL_IMPLICIT; import static android.app.ActivityManager.PROCESS_CAPABILITY_BFSL; +import static android.app.ActivityManager.PROCESS_CAPABILITY_CPU_TIME; import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_AUDIO_CONTROL; import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_CAMERA; import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_LOCATION; @@ -155,6 +156,7 @@ import android.os.Process; import android.os.RemoteException; import android.os.SystemClock; import android.os.Trace; +import android.os.UserHandle; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Slog; @@ -469,7 +471,6 @@ public class OomAdjuster { } Process.setThreadPriority(tid, priority); } - } // TODO(b/346822474): hook up global state usage. @@ -499,7 +500,8 @@ public class OomAdjuster { } OomAdjuster(ActivityManagerService service, ProcessList processList, ActiveUids activeUids, - ServiceThread adjusterThread, GlobalState globalState, Injector injector) { + ServiceThread adjusterThread, GlobalState globalState, + CachedAppOptimizer cachedAppOptimizer, Injector injector) { mService = service; mGlobalState = globalState; mInjector = injector; @@ -508,7 +510,7 @@ public class OomAdjuster { mActiveUids = activeUids; mConstants = mService.mConstants; - mCachedAppOptimizer = new CachedAppOptimizer(mService); + mCachedAppOptimizer = cachedAppOptimizer; mCacheOomRanker = new CacheOomRanker(service); mLogger = new OomAdjusterDebugLogger(this, mService.mConstants); @@ -2597,6 +2599,7 @@ public class OomAdjuster { } capability |= getDefaultCapability(app, procState); + capability |= getCpuCapability(app, now); // Procstates below BFGS should never have this capability. if (procState > PROCESS_STATE_BOUND_FOREGROUND_SERVICE) { @@ -2739,8 +2742,12 @@ public class OomAdjuster { if (app.mOptRecord.setShouldNotFreeze(true, dryRun, app.mOptRecord.shouldNotFreezeReason() | client.mOptRecord.shouldNotFreezeReason(), mAdjSeq)) { - // Bail out early, as we only care about the return value for a dryrun. - return true; + if (Flags.useCpuTimeCapability()) { + // Do nothing, capability updated check will handle the dryrun output. + } else { + // Bail out early, as we only care about the return value for a dryrun. + return true; + } } } @@ -2751,6 +2758,8 @@ public class OomAdjuster { // we check the final procstate, and remove it if the procsate is below BFGS. capability |= getBfslCapabilityFromClient(client); + capability |= getCpuCapabilityFromClient(client); + if (cr.notHasFlag(Context.BIND_WAIVE_PRIORITY)) { if (cr.hasFlag(Context.BIND_INCLUDE_CAPABILITIES)) { capability |= cstate.getCurCapability(); @@ -2809,9 +2818,14 @@ public class OomAdjuster { app.mOptRecord.shouldNotFreezeReason() | ProcessCachedOptimizerRecord .SHOULD_NOT_FREEZE_REASON_BINDER_ALLOW_OOM_MANAGEMENT, mAdjSeq)) { - // Bail out early, as we only care about the return value for a dryrun. - return true; + if (Flags.useCpuTimeCapability()) { + // Do nothing, capability updated check will handle the dryrun output. + } else { + // Bail out early, as we only care about the return value for a dryrun. + return true; + } } + capability |= PROCESS_CAPABILITY_CPU_TIME; } // Not doing bind OOM management, so treat // this guy more like a started service. @@ -3053,9 +3067,14 @@ public class OomAdjuster { app.mOptRecord.shouldNotFreezeReason() | ProcessCachedOptimizerRecord .SHOULD_NOT_FREEZE_REASON_BIND_WAIVE_PRIORITY, mAdjSeq)) { - // Bail out early, as we only care about the return value for a dryrun. - return true; + if (Flags.useCpuTimeCapability()) { + // Do nothing, capability updated check will handle the dryrun output. + } else { + // Bail out early, as we only care about the return value for a dryrun. + return true; + } } + capability |= PROCESS_CAPABILITY_CPU_TIME; } } if (cr.hasFlag(Context.BIND_TREAT_LIKE_ACTIVITY)) { @@ -3108,9 +3127,24 @@ public class OomAdjuster { capability &= ~PROCESS_CAPABILITY_BFSL; } if (!updated) { - updated = adj < prevRawAdj || procState < prevProcState || schedGroup > prevSchedGroup - || (capability != prevCapability - && (capability & prevCapability) == prevCapability); + if (adj < prevRawAdj || procState < prevProcState || schedGroup > prevSchedGroup) { + updated = true; + } + + if (Flags.useCpuTimeCapability()) { + if ((capability != prevCapability) + && ((capability & prevCapability) == prevCapability)) { + updated = true; + } + } else { + // Ignore PROCESS_CAPABILITY_CPU_TIME in capability comparison + final int curFiltered = capability & ~PROCESS_CAPABILITY_CPU_TIME; + final int prevFiltered = prevCapability & ~PROCESS_CAPABILITY_CPU_TIME; + if ((curFiltered != prevFiltered) + && ((curFiltered & prevFiltered) == prevFiltered)) { + updated = true; + } + } } if (dryRun) { @@ -3186,6 +3220,8 @@ public class OomAdjuster { // we check the final procstate, and remove it if the procsate is below BFGS. capability |= getBfslCapabilityFromClient(client); + capability |= getCpuCapabilityFromClient(client); + if (clientProcState >= PROCESS_STATE_CACHED_ACTIVITY) { // If the other app is cached for any reason, for purposes here // we are going to consider it empty. @@ -3196,8 +3232,12 @@ public class OomAdjuster { if (app.mOptRecord.setShouldNotFreeze(true, dryRun, app.mOptRecord.shouldNotFreezeReason() | client.mOptRecord.shouldNotFreezeReason(), mAdjSeq)) { - // Bail out early, as we only care about the return value for a dryrun. - return true; + if (Flags.useCpuTimeCapability()) { + // Do nothing, capability updated check will handle the dryrun output. + } else { + // Bail out early, as we only care about the return value for a dryrun. + return true; + } } } @@ -3273,10 +3313,25 @@ public class OomAdjuster { capability &= ~PROCESS_CAPABILITY_BFSL; } - if (dryRun && (adj < prevRawAdj || procState < prevProcState || schedGroup > prevSchedGroup - || (capability != prevCapability - && (capability & prevCapability) == prevCapability))) { - return true; + if (dryRun) { + if (adj < prevRawAdj || procState < prevProcState || schedGroup > prevSchedGroup) { + return true; + } + + if (Flags.useCpuTimeCapability()) { + if ((capability != prevCapability) + && ((capability & prevCapability) == prevCapability)) { + return true; + } + } else { + // Ignore PROCESS_CAPABILITY_CPU_TIME in capability comparison + final int curFiltered = capability & ~PROCESS_CAPABILITY_CPU_TIME; + final int prevFiltered = prevCapability & ~PROCESS_CAPABILITY_CPU_TIME; + if ((curFiltered != prevFiltered) + && ((curFiltered & prevFiltered) == prevFiltered)) { + return true; + } + } } if (adj < prevRawAdj) { @@ -3328,6 +3383,29 @@ public class OomAdjuster { return baseCapabilities | networkCapabilities; } + private static int getCpuCapability(ProcessRecord app, long nowUptime) { + final UidRecord uidRec = app.getUidRecord(); + if (uidRec != null && uidRec.isCurAllowListed()) { + // Process has user visible activities. + return PROCESS_CAPABILITY_CPU_TIME; + } + if (UserHandle.isCore(app.uid)) { + // Make sure all system components are not frozen. + return PROCESS_CAPABILITY_CPU_TIME; + } + if (app.mState.getCachedHasVisibleActivities()) { + // Process has user visible activities. + return PROCESS_CAPABILITY_CPU_TIME; + } + if (app.mServices.hasUndemotedShortForegroundService(nowUptime)) { + // It running a short fgs, just give it cpu time. + return PROCESS_CAPABILITY_CPU_TIME; + } + // TODO(b/370817323): Populate this method with all of the reasons to keep a process + // unfrozen. + return 0; + } + /** * @return the BFSL capability from a client (of a service binding or provider). */ @@ -3376,6 +3454,15 @@ public class OomAdjuster { } /** + * @return the CPU capability from a client (of a service binding or provider). + */ + private static int getCpuCapabilityFromClient(ProcessRecord client) { + // Just grant CPU capability every time + // TODO(b/370817323): Populate with reasons to not propagate cpu capability across bindings. + return client.mState.getCurCapability() & PROCESS_CAPABILITY_CPU_TIME; + } + + /** * Checks if for the given app and client, there's a cycle that should skip over the client * for now or use partial values to evaluate the effect of the client binding. * @param app @@ -3955,6 +4042,39 @@ public class OomAdjuster { mCacheOomRanker.dump(pw); } + /** + * Return whether or not a process should be frozen. + */ + boolean getFreezePolicy(ProcessRecord proc) { + // Reasons to not freeze: + if (Flags.useCpuTimeCapability()) { + if ((proc.mState.getCurCapability() & PROCESS_CAPABILITY_CPU_TIME) != 0) { + /// App is important enough (see {@link #getCpuCapability}) or bound by something + /// important enough to not be frozen. + return false; + } + } else { + // The CPU capability handling covers all setShouldNotFreeze paths. Must check + // shouldNotFreeze, if the CPU capability is not being used. + if (proc.mOptRecord.shouldNotFreeze()) { + return false; + } + } + + if (proc.mOptRecord.isFreezeExempt()) { + return false; + } + + // Reasons to freeze: + if (proc.mState.getCurAdj() >= FREEZER_CUTOFF_ADJ) { + // Oomscore is in a high enough state, it is safe to freeze. + return true; + } + + // Default, do not freeze a process. + return false; + } + @GuardedBy({"mService", "mProcLock"}) void updateAppFreezeStateLSP(ProcessRecord app, @OomAdjReason int oomAdjReason, boolean immediate, int oldOomAdj) { @@ -3969,43 +4089,44 @@ public class OomAdjuster { (state.getCurAdj() >= FREEZER_CUTOFF_ADJ ^ oldOomAdj >= FREEZER_CUTOFF_ADJ) || oldOomAdj == UNKNOWN_ADJ; final boolean shouldNotFreezeChanged = opt.shouldNotFreezeAdjSeq() == mAdjSeq; - if ((oomAdjChanged || shouldNotFreezeChanged) + final boolean hasCpuCapability = + (PROCESS_CAPABILITY_CPU_TIME & app.mState.getCurCapability()) + == PROCESS_CAPABILITY_CPU_TIME; + final boolean usedToHaveCpuCapability = + (PROCESS_CAPABILITY_CPU_TIME & app.mState.getSetCapability()) + == PROCESS_CAPABILITY_CPU_TIME; + final boolean cpuCapabilityChanged = hasCpuCapability != usedToHaveCpuCapability; + if ((oomAdjChanged || shouldNotFreezeChanged || cpuCapabilityChanged) && Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) { Trace.instantForTrack(Trace.TRACE_TAG_ACTIVITY_MANAGER, CachedAppOptimizer.ATRACE_FREEZER_TRACK, "updateAppFreezeStateLSP " + app.processName + + " pid: " + app.getPid() + " isFreezeExempt: " + opt.isFreezeExempt() + " isFrozen: " + opt.isFrozen() + " shouldNotFreeze: " + opt.shouldNotFreeze() + " shouldNotFreezeReason: " + opt.shouldNotFreezeReason() + " curAdj: " + state.getCurAdj() + " oldOomAdj: " + oldOomAdj - + " immediate: " + immediate); + + " immediate: " + immediate + + " cpuCapability: " + hasCpuCapability); } } - if (app.mOptRecord.isFreezeExempt()) { - return; - } - - // if an app is already frozen and shouldNotFreeze becomes true, immediately unfreeze - if (opt.isFrozen() && opt.shouldNotFreeze()) { - mCachedAppOptimizer.unfreezeAppLSP(app, - CachedAppOptimizer.getUnfreezeReasonCodeFromOomAdjReason(oomAdjReason)); - return; - } - - // Use current adjustment when freezing, set adjustment when unfreezing. - if (state.getCurAdj() >= FREEZER_CUTOFF_ADJ && !opt.isFrozen() - && !opt.shouldNotFreeze()) { - if (!immediate) { - mCachedAppOptimizer.freezeAppAsyncLSP(app); - } else { + if (getFreezePolicy(app)) { + // This process should be frozen. + if (immediate && !opt.isFrozen()) { + // And it will be frozen immediately. mCachedAppOptimizer.freezeAppAsyncAtEarliestLSP(app); + } else if (!opt.isFrozen() || !opt.isPendingFreeze()) { + mCachedAppOptimizer.freezeAppAsyncLSP(app); + } + } else { + // This process should not be frozen. + if (opt.isFrozen() || opt.isPendingFreeze()) { + mCachedAppOptimizer.unfreezeAppLSP(app, + CachedAppOptimizer.getUnfreezeReasonCodeFromOomAdjReason(oomAdjReason)); } - } else if (state.getSetAdj() < FREEZER_CUTOFF_ADJ) { - mCachedAppOptimizer.unfreezeAppLSP(app, - CachedAppOptimizer.getUnfreezeReasonCodeFromOomAdjReason(oomAdjReason)); } } @@ -4029,7 +4150,8 @@ public class OomAdjuster { final int size = processes.size(); for (int i = 0; i < size; i++) { ProcessRecord proc = processes.get(i); - mCachedAppOptimizer.unfreezeTemporarily(proc, reason); + mCachedAppOptimizer.unfreezeTemporarily(proc, + CachedAppOptimizer.getUnfreezeReasonCodeFromOomAdjReason(reason)); } processes.clear(); } diff --git a/services/core/java/com/android/server/am/OomAdjusterModernImpl.java b/services/core/java/com/android/server/am/OomAdjusterModernImpl.java index 8b660559f550..1b7e8f0bd244 100644 --- a/services/core/java/com/android/server/am/OomAdjusterModernImpl.java +++ b/services/core/java/com/android/server/am/OomAdjusterModernImpl.java @@ -758,8 +758,9 @@ public class OomAdjusterModernImpl extends OomAdjuster { OomAdjusterModernImpl(ActivityManagerService service, ProcessList processList, ActiveUids activeUids, ServiceThread adjusterThread, GlobalState globalState, - Injector injector) { - super(service, processList, activeUids, adjusterThread, globalState, injector); + CachedAppOptimizer cachedAppOptimizer, Injector injector) { + super(service, processList, activeUids, adjusterThread, globalState, cachedAppOptimizer, + injector); } private final ProcessRecordNodes mProcessRecordProcStateNodes = new ProcessRecordNodes( diff --git a/services/core/java/com/android/server/am/ProcessServiceRecord.java b/services/core/java/com/android/server/am/ProcessServiceRecord.java index 5cb8b954a2ba..364497491785 100644 --- a/services/core/java/com/android/server/am/ProcessServiceRecord.java +++ b/services/core/java/com/android/server/am/ProcessServiceRecord.java @@ -256,18 +256,24 @@ final class ProcessServiceRecord { } // Now we need to look at all short-FGS within the process and see if all of them are // procstate-timed-out or not. + return !hasUndemotedShortForegroundService(nowUptime); + } + + boolean hasUndemotedShortForegroundService(long nowUptime) { for (int i = mServices.size() - 1; i >= 0; i--) { final ServiceRecord sr = mServices.valueAt(i); if (!sr.isShortFgs() || !sr.hasShortFgsInfo()) { continue; } if (sr.getShortFgsInfo().getProcStateDemoteTime() >= nowUptime) { - return false; + // This short fgs has not timed out yet. + return true; } } - return true; + return false; } + int getReportedForegroundServiceTypes() { return mRepFgServiceTypes; } diff --git a/services/core/java/com/android/server/am/ProcessStateController.java b/services/core/java/com/android/server/am/ProcessStateController.java index 01468c640f6c..57899228e6ad 100644 --- a/services/core/java/com/android/server/am/ProcessStateController.java +++ b/services/core/java/com/android/server/am/ProcessStateController.java @@ -29,6 +29,7 @@ import android.util.Slog; import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import com.android.server.ServiceThread; /** @@ -44,13 +45,14 @@ public class ProcessStateController { private final GlobalState mGlobalState = new GlobalState(); private ProcessStateController(ActivityManagerService ams, ProcessList processList, - ActiveUids activeUids, ServiceThread handlerThread, OomAdjuster.Injector oomAdjInjector, + ActiveUids activeUids, ServiceThread handlerThread, + CachedAppOptimizer cachedAppOptimizer, OomAdjuster.Injector oomAdjInjector, boolean useOomAdjusterModernImpl) { mOomAdjuster = useOomAdjusterModernImpl ? new OomAdjusterModernImpl(ams, processList, activeUids, handlerThread, - mGlobalState, oomAdjInjector) + mGlobalState, cachedAppOptimizer, oomAdjInjector) : new OomAdjuster(ams, processList, activeUids, handlerThread, mGlobalState, - oomAdjInjector); + cachedAppOptimizer, oomAdjInjector); } /** @@ -594,6 +596,7 @@ public class ProcessStateController { private final ActiveUids mActiveUids; private ServiceThread mHandlerThread = null; + private CachedAppOptimizer mCachedAppOptimizer = null; private OomAdjuster.Injector mOomAdjInjector = null; private boolean mUseOomAdjusterModernImpl = false; @@ -610,24 +613,38 @@ public class ProcessStateController { if (mHandlerThread == null) { mHandlerThread = OomAdjuster.createAdjusterThread(); } + if (mCachedAppOptimizer == null) { + mCachedAppOptimizer = new CachedAppOptimizer(mAms); + } if (mOomAdjInjector == null) { mOomAdjInjector = new OomAdjuster.Injector(); } return new ProcessStateController(mAms, mProcessList, mActiveUids, mHandlerThread, - mOomAdjInjector, mUseOomAdjusterModernImpl); + mCachedAppOptimizer, mOomAdjInjector, mUseOomAdjusterModernImpl); } /** * For Testing Purposes. Set what thread OomAdjuster will offload tasks on to. */ + @VisibleForTesting public Builder setHandlerThread(ServiceThread handlerThread) { mHandlerThread = handlerThread; return this; } /** + * For Testing Purposes. Set the CachedAppOptimzer used by OomAdjuster. + */ + @VisibleForTesting + public Builder setCachedAppOptimizer(CachedAppOptimizer cachedAppOptimizer) { + mCachedAppOptimizer = cachedAppOptimizer; + return this; + } + + /** * For Testing Purposes. Set an injector for OomAdjuster. */ + @VisibleForTesting public Builder setOomAdjusterInjector(OomAdjuster.Injector injector) { mOomAdjInjector = injector; return this; diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java index 8dc7c7345f79..3dd5ec9a3834 100644 --- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java +++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java @@ -42,6 +42,7 @@ import android.aconfigd.Aconfigd.StorageReturnMessages; import static com.android.aconfig_new_storage.Flags.enableAconfigStorageDaemon; import static com.android.aconfig_new_storage.Flags.supportImmediateLocalOverrides; import static com.android.aconfig_new_storage.Flags.supportClearLocalOverridesImmediately; +import static com.android.aconfig.flags.Flags.enableSystemAconfigdRust; import java.io.DataInputStream; import java.io.DataOutputStream; @@ -144,7 +145,6 @@ public class SettingsToPropertiesMapper { "android_core_networking", "android_health_services", "android_sdk", - "android_stylus", "aoc", "app_widgets", "arc_next", @@ -209,6 +209,7 @@ public class SettingsToPropertiesMapper { "pixel_continuity", "pixel_perf", "pixel_sensors", + "pixel_state_server", "pixel_system_sw_video", "pixel_video_sw", "pixel_watch", @@ -456,9 +457,11 @@ public class SettingsToPropertiesMapper { static ProtoInputStream sendAconfigdRequests(ProtoOutputStream requests) { // connect to aconfigd socket LocalSocket client = new LocalSocket(); + String socketName = enableSystemAconfigdRust() + ? "aconfigd_system" : "aconfigd"; try{ client.connect(new LocalSocketAddress( - "aconfigd", LocalSocketAddress.Namespace.RESERVED)); + socketName, LocalSocketAddress.Namespace.RESERVED)); Slog.d(TAG, "connected to aconfigd socket"); } catch (IOException ioe) { logErr("failed to connect to aconfigd socket", ioe); diff --git a/services/core/java/com/android/server/am/broadcasts_flags.aconfig b/services/core/java/com/android/server/am/broadcasts_flags.aconfig index b1185d552941..7f169db7dcec 100644 --- a/services/core/java/com/android/server/am/broadcasts_flags.aconfig +++ b/services/core/java/com/android/server/am/broadcasts_flags.aconfig @@ -7,4 +7,12 @@ flag { description: "Restrict priority values defined by non-system apps" is_fixed_read_only: true bug: "369487976" +} + +flag { + name: "limit_priority_scope" + namespace: "backstage_power" + description: "Limit the scope of receiver priorities to within a process" + is_fixed_read_only: true + bug: "369487976" }
\ No newline at end of file diff --git a/services/core/java/com/android/server/am/flags.aconfig b/services/core/java/com/android/server/am/flags.aconfig index 5d5b35b8c4fb..711b163ea424 100644 --- a/services/core/java/com/android/server/am/flags.aconfig +++ b/services/core/java/com/android/server/am/flags.aconfig @@ -90,14 +90,6 @@ flag { flag { namespace: "backstage_power" - name: "use_permission_manager_for_broadcast_delivery_check" - description: "Use PermissionManager API for broadcast delivery permission checks." - bug: "315468967" - is_fixed_read_only: true -} - -flag { - namespace: "backstage_power" name: "trace_receiver_registration" description: "Add tracing for broadcast receiver registration and un-registration" bug: "336385821" @@ -261,3 +253,10 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "use_cpu_time_capability" + namespace: "backstage_power" + description: "Use PROCESS_CAPABILITY_CPU_TIME to control unfreeze state." + bug: "370817323" +} diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java index 09de89445122..34d4fb02ad99 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java +++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java @@ -40,6 +40,7 @@ import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothLeAudio; import android.bluetooth.BluetoothProfile; import android.content.Intent; +import android.media.AudioDescriptor; import android.media.AudioDeviceAttributes; import android.media.AudioDeviceInfo; import android.media.AudioDevicePort; @@ -47,6 +48,7 @@ import android.media.AudioFormat; import android.media.AudioManager; import android.media.AudioManager.AudioDeviceCategory; import android.media.AudioPort; +import android.media.AudioProfile; import android.media.AudioRoutesInfo; import android.media.AudioSystem; import android.media.IAudioRoutesObserver; @@ -619,6 +621,8 @@ public class AudioDeviceInventory { final int mGroupId; @NonNull String mPeerDeviceAddress; @NonNull String mPeerIdentityDeviceAddress; + @NonNull List<AudioProfile> mAudioProfiles; + @NonNull List<AudioDescriptor> mAudioDescriptors; /** Disabled operating modes for this device. Use a negative logic so that by default * an empty list means all modes are allowed. @@ -627,7 +631,8 @@ public class AudioDeviceInventory { DeviceInfo(int deviceType, String deviceName, String address, String identityAddress, int codecFormat, - int groupId, String peerAddress, String peerIdentityAddress) { + int groupId, String peerAddress, String peerIdentityAddress, + List<AudioProfile> profiles, List<AudioDescriptor> descriptors) { mDeviceType = deviceType; mDeviceName = TextUtils.emptyIfNull(deviceName); mDeviceAddress = TextUtils.emptyIfNull(address); @@ -639,6 +644,16 @@ public class AudioDeviceInventory { mGroupId = groupId; mPeerDeviceAddress = TextUtils.emptyIfNull(peerAddress); mPeerIdentityDeviceAddress = TextUtils.emptyIfNull(peerIdentityAddress); + mAudioProfiles = profiles; + mAudioDescriptors = descriptors; + } + + DeviceInfo(int deviceType, String deviceName, String address, + String identityAddress, int codecFormat, + int groupId, String peerAddress, String peerIdentityAddress) { + this(deviceType, deviceName, address, identityAddress, codecFormat, + groupId, peerAddress, peerIdentityAddress, + new ArrayList<>(), new ArrayList<>()); } /** Constructor for all devices except A2DP sink and LE Audio */ @@ -646,6 +661,13 @@ public class AudioDeviceInventory { this(deviceType, deviceName, address, null, AudioSystem.AUDIO_FORMAT_DEFAULT); } + /** Constructor for HDMI OUT, HDMI ARC/EARC sink devices */ + DeviceInfo(int deviceType, String deviceName, String address, + List<AudioProfile> profiles, List<AudioDescriptor> descriptors) { + this(deviceType, deviceName, address, null, AudioSystem.AUDIO_FORMAT_DEFAULT, + BluetoothLeAudio.GROUP_ID_INVALID, null, null, profiles, descriptors); + } + /** Constructor for A2DP sink devices */ DeviceInfo(int deviceType, String deviceName, String address, String identityAddress, int codecFormat) { @@ -1194,27 +1216,31 @@ public class AudioDeviceInventory { } /*package*/ void onToggleHdmi() { - MediaMetrics.Item mmi = new MediaMetrics.Item(mMetricsId + "onToggleHdmi") - .set(MediaMetrics.Property.DEVICE, - AudioSystem.getDeviceName(AudioSystem.DEVICE_OUT_HDMI)); + final int[] hdmiDevices = { AudioSystem.DEVICE_OUT_HDMI, + AudioSystem.DEVICE_OUT_HDMI_ARC, AudioSystem.DEVICE_OUT_HDMI_EARC }; + synchronized (mDevicesLock) { - // Is HDMI connected? - final String key = DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_HDMI, ""); - final DeviceInfo di = mConnectedDevices.get(key); - if (di == null) { - Log.e(TAG, "invalid null DeviceInfo in onToggleHdmi"); - mmi.set(MediaMetrics.Property.EARLY_RETURN, "invalid null DeviceInfo").record(); - return; + for (DeviceInfo di : mConnectedDevices.values()) { + boolean isHdmiDevice = Arrays.stream(hdmiDevices).anyMatch(device -> + device == di.mDeviceType); + if (isHdmiDevice) { + MediaMetrics.Item mmi = new MediaMetrics.Item(mMetricsId + "onToggleHdmi") + .set(MediaMetrics.Property.DEVICE, + AudioSystem.getDeviceName(di.mDeviceType)); + AudioDeviceAttributes ada = new AudioDeviceAttributes( + AudioDeviceAttributes.ROLE_OUTPUT, + AudioDeviceInfo.convertInternalDeviceToDeviceType(di.mDeviceType), + di.mDeviceAddress, di.mDeviceName, di.mAudioProfiles, + di.mAudioDescriptors); + // Toggle HDMI to retrigger broadcast with proper formats. + setWiredDeviceConnectionState(ada, + AudioSystem.DEVICE_STATE_UNAVAILABLE, "onToggleHdmi"); // disconnect + setWiredDeviceConnectionState(ada, + AudioSystem.DEVICE_STATE_AVAILABLE, "onToggleHdmi"); // reconnect + mmi.record(); + } } - // Toggle HDMI to retrigger broadcast with proper formats. - setWiredDeviceConnectionState( - new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_HDMI, ""), - AudioSystem.DEVICE_STATE_UNAVAILABLE, "android"); // disconnect - setWiredDeviceConnectionState( - new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_HDMI, ""), - AudioSystem.DEVICE_STATE_AVAILABLE, "android"); // reconnect } - mmi.record(); } @GuardedBy("mDevicesLock") @@ -1818,7 +1844,15 @@ public class AudioDeviceInventory { .printSlog(EventLogger.Event.ALOGE, TAG)); return false; } - mConnectedDevices.put(deviceKey, new DeviceInfo(device, deviceName, address)); + + if (device == AudioSystem.DEVICE_OUT_HDMI || + device == AudioSystem.DEVICE_OUT_HDMI_ARC || + device == AudioSystem.DEVICE_OUT_HDMI_EARC) { + mConnectedDevices.put(deviceKey, new DeviceInfo(device, deviceName, + address, attributes.getAudioProfiles(), attributes.getAudioDescriptors())); + } else { + mConnectedDevices.put(deviceKey, new DeviceInfo(device, deviceName, address)); + } mDeviceBroker.postAccessoryPlugMediaUnmute(device); status = true; } else if (!connect && isConnected) { diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 0cf55bb2bf17..6ba356990cac 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -9193,16 +9193,6 @@ public class AudioService extends IAudioService.Stub mIndexMin = MIN_STREAM_VOLUME[streamType] * 10; mIndexMax = MAX_STREAM_VOLUME[streamType] * 10; - final int status = AudioSystem.initStreamVolume( - streamType, MIN_STREAM_VOLUME[streamType], MAX_STREAM_VOLUME[streamType]); - if (status != AudioSystem.AUDIO_STATUS_OK) { - sLifecycleLogger.enqueue(new EventLogger.StringEvent( - "VSS() stream:" + streamType + " initStreamVolume=" + status) - .printLog(ALOGE, TAG)); - sendMsg(mAudioHandler, MSG_REINIT_VOLUMES, SENDMSG_NOOP, 0, 0, - "VSS()" /*obj*/, 2 * INDICATE_SYSTEM_READY_RETRY_DELAY_MS); - } - updateIndexFactors(); mIndexMinNoPerm = mIndexMin; // may be overwritten later in updateNoPermMinIndex() @@ -9267,6 +9257,19 @@ public class AudioService extends IAudioService.Stub mIndexMinNoPerm = mIndexMin; } } + + final int status = AudioSystem.initStreamVolume( + mStreamType, mIndexMin / 10, mIndexMax / 10); + sVolumeLogger.enqueue(new EventLogger.StringEvent( + "updateIndexFactors() stream:" + mStreamType + " index min/max:" + + mIndexMin / 10 + "/" + mIndexMax / 10 + " indexStepFactor:" + + mIndexStepFactor).printSlog(ALOGI, TAG)); + if (status != AudioSystem.AUDIO_STATUS_OK) { + sVolumeLogger.enqueue(new EventLogger.StringEvent( + "Failed initStreamVolume with status=" + status).printSlog(ALOGE, TAG)); + sendMsg(mAudioHandler, MSG_REINIT_VOLUMES, SENDMSG_NOOP, 0, 0, + "updateIndexFactors()" /*obj*/, 2 * INDICATE_SYSTEM_READY_RETRY_DELAY_MS); + } } /** diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java index 00280c8f9c04..65780238ede4 100644 --- a/services/core/java/com/android/server/biometrics/BiometricService.java +++ b/services/core/java/com/android/server/biometrics/BiometricService.java @@ -288,11 +288,13 @@ public class BiometricService extends SystemService { * @param handler The handler to run {@link #onChange} on, or null if none. */ public SettingObserver(Context context, Handler handler, - List<BiometricService.EnabledOnKeyguardCallback> callbacks) { + List<BiometricService.EnabledOnKeyguardCallback> callbacks, + UserManager userManager, FingerprintManager fingerprintManager, + FaceManager faceManager) { super(handler); mContentResolver = context.getContentResolver(); mCallbacks = callbacks; - mUserManager = context.getSystemService(UserManager.class); + mUserManager = userManager; final boolean hasFingerprint = context.getPackageManager() .hasSystemFeature(PackageManager.FEATURE_FINGERPRINT); @@ -304,7 +306,7 @@ public class BiometricService extends SystemService { Build.VERSION.DEVICE_INITIAL_SDK_INT <= Build.VERSION_CODES.Q && hasFace && !hasFingerprint; - addBiometricListenersForMandatoryBiometrics(context); + addBiometricListenersForMandatoryBiometrics(context, fingerprintManager, faceManager); updateContentObserver(); } @@ -431,11 +433,21 @@ public class BiometricService extends SystemService { public boolean getMandatoryBiometricsEnabledAndRequirementsSatisfiedForUser(int userId) { if (!mMandatoryBiometricsEnabled.containsKey(userId)) { + Slog.d(TAG, "update mb toggle for user " + userId); updateMandatoryBiometricsForAllProfiles(userId); } if (!mMandatoryBiometricsRequirementsSatisfied.containsKey(userId)) { + Slog.d(TAG, "update mb reqs for user " + userId); updateMandatoryBiometricsRequirementsForAllProfiles(userId); } + + Slog.d(TAG, mMandatoryBiometricsEnabled.getOrDefault(userId, + DEFAULT_MANDATORY_BIOMETRICS_STATUS) + + " " + mMandatoryBiometricsRequirementsSatisfied.getOrDefault(userId, + DEFAULT_MANDATORY_BIOMETRICS_REQUIREMENTS_SATISFIED_STATUS) + + " " + getEnabledForApps(userId) + + " " + (mFingerprintEnrolledForUser.getOrDefault(userId, false /* default */) + || mFaceEnrolledForUser.getOrDefault(userId, false /* default */))); return mMandatoryBiometricsEnabled.getOrDefault(userId, DEFAULT_MANDATORY_BIOMETRICS_STATUS) && mMandatoryBiometricsRequirementsSatisfied.getOrDefault(userId, @@ -456,11 +468,23 @@ public class BiometricService extends SystemService { private void updateMandatoryBiometricsForAllProfiles(int userId) { int effectiveUserId = userId; - if (mUserManager.getMainUser() != null) { - effectiveUserId = mUserManager.getMainUser().getIdentifier(); + final UserInfo parentProfile = mUserManager.getProfileParent(userId); + + if (parentProfile != null) { + effectiveUserId = parentProfile.id; } - for (int profileUserId: mUserManager.getEnabledProfileIds(effectiveUserId)) { - mMandatoryBiometricsEnabled.put(profileUserId, + + final int[] enabledProfileIds = mUserManager.getEnabledProfileIds(effectiveUserId); + if (enabledProfileIds != null) { + for (int profileUserId : enabledProfileIds) { + mMandatoryBiometricsEnabled.put(profileUserId, + Settings.Secure.getIntForUser( + mContentResolver, Settings.Secure.MANDATORY_BIOMETRICS, + DEFAULT_MANDATORY_BIOMETRICS_STATUS ? 1 : 0, + effectiveUserId) != 0); + } + } else { + mMandatoryBiometricsEnabled.put(userId, Settings.Secure.getIntForUser( mContentResolver, Settings.Secure.MANDATORY_BIOMETRICS, DEFAULT_MANDATORY_BIOMETRICS_STATUS ? 1 : 0, @@ -470,11 +494,24 @@ public class BiometricService extends SystemService { private void updateMandatoryBiometricsRequirementsForAllProfiles(int userId) { int effectiveUserId = userId; - if (mUserManager.getMainUser() != null) { - effectiveUserId = mUserManager.getMainUser().getIdentifier(); + final UserInfo parentProfile = mUserManager.getProfileParent(userId); + + if (parentProfile != null) { + effectiveUserId = parentProfile.id; } - for (int profileUserId: mUserManager.getEnabledProfileIds(effectiveUserId)) { - mMandatoryBiometricsRequirementsSatisfied.put(profileUserId, + + final int[] enabledProfileIds = mUserManager.getEnabledProfileIds(effectiveUserId); + if (enabledProfileIds != null) { + for (int profileUserId : enabledProfileIds) { + mMandatoryBiometricsRequirementsSatisfied.put(profileUserId, + Settings.Secure.getIntForUser(mContentResolver, + Settings.Secure.MANDATORY_BIOMETRICS_REQUIREMENTS_SATISFIED, + DEFAULT_MANDATORY_BIOMETRICS_REQUIREMENTS_SATISFIED_STATUS + ? 1 : 0, + effectiveUserId) != 0); + } + } else { + mMandatoryBiometricsRequirementsSatisfied.put(userId, Settings.Secure.getIntForUser(mContentResolver, Settings.Secure.MANDATORY_BIOMETRICS_REQUIREMENTS_SATISFIED, DEFAULT_MANDATORY_BIOMETRICS_REQUIREMENTS_SATISFIED_STATUS ? 1 : 0, @@ -482,10 +519,8 @@ public class BiometricService extends SystemService { } } - private void addBiometricListenersForMandatoryBiometrics(Context context) { - final FingerprintManager fingerprintManager = context.getSystemService( - FingerprintManager.class); - final FaceManager faceManager = context.getSystemService(FaceManager.class); + private void addBiometricListenersForMandatoryBiometrics(Context context, + FingerprintManager fingerprintManager, FaceManager faceManager) { if (fingerprintManager != null) { fingerprintManager.addAuthenticatorsRegisteredCallback( new IFingerprintAuthenticatorsRegisteredCallback.Stub() { @@ -1169,7 +1204,9 @@ public class BiometricService extends SystemService { */ public SettingObserver getSettingObserver(Context context, Handler handler, List<EnabledOnKeyguardCallback> callbacks) { - return new SettingObserver(context, handler, callbacks); + return new SettingObserver(context, handler, callbacks, context.getSystemService( + UserManager.class), context.getSystemService(FingerprintManager.class), + context.getSystemService(FaceManager.class)); } /** diff --git a/services/core/java/com/android/server/biometrics/PreAuthInfo.java b/services/core/java/com/android/server/biometrics/PreAuthInfo.java index e8fa41749473..afdc0c0294a6 100644 --- a/services/core/java/com/android/server/biometrics/PreAuthInfo.java +++ b/services/core/java/com/android/server/biometrics/PreAuthInfo.java @@ -119,9 +119,16 @@ class PreAuthInfo { == BiometricManager.Authenticators.IDENTITY_CHECK; boolean isMandatoryBiometricsAuthentication = false; + final int effectiveUserId; + if (Flags.effectiveUserBp()) { + effectiveUserId = userManager.getCredentialOwnerProfile(userId); + } else { + effectiveUserId = userId; + } + if (dropCredentialFallback(promptInfo.getAuthenticators(), settingObserver.getMandatoryBiometricsEnabledAndRequirementsSatisfiedForUser( - userId), trustManager)) { + effectiveUserId), trustManager)) { isMandatoryBiometricsAuthentication = true; promptInfo.setAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG); if (promptInfo.getNegativeButtonText() == null) { @@ -145,13 +152,6 @@ class PreAuthInfo { final List<BiometricSensor> eligibleSensors = new ArrayList<>(); final List<Pair<BiometricSensor, Integer>> ineligibleSensors = new ArrayList<>(); - final int effectiveUserId; - if (Flags.effectiveUserBp()) { - effectiveUserId = userManager.getCredentialOwnerProfile(userId); - } else { - effectiveUserId = userId; - } - if (biometricRequested) { for (BiometricSensor sensor : sensors) { diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java index 97f4a5c570b5..eeac26031719 100644 --- a/services/core/java/com/android/server/compat/PlatformCompat.java +++ b/services/core/java/com/android/server/compat/PlatformCompat.java @@ -32,6 +32,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.net.Uri; import android.os.Binder; @@ -75,6 +76,7 @@ public class PlatformCompat extends IPlatformCompat.Stub { private final ChangeReporter mChangeReporter; private final CompatConfig mCompatConfig; private final AndroidBuildClassifier mBuildClassifier; + private Boolean mIsWear; public PlatformCompat(Context context) { super(PermissionEnforcer.fromContext(context)); @@ -511,9 +513,16 @@ public class PlatformCompat extends IPlatformCompat.Stub { } private ApplicationInfo fixTargetSdk(ApplicationInfo appInfo, int uid) { + + // mIsWear doesn't need to be locked, ok if executes twice + if (mIsWear == null) { + mIsWear = mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH); + } + // b/282922910 - we don't want apps sharing system uid and targeting // older target sdk to impact all system uid apps - if (Flags.systemUidTargetSystemSdk() && uid == Process.SYSTEM_UID) { + if (Flags.systemUidTargetSystemSdk() && !mIsWear && + uid == Process.SYSTEM_UID) { appInfo.targetSdkVersion = Build.VERSION.SDK_INT; } return appInfo; diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java index 4ad7c10a1444..d2c044fdbb5e 100644 --- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java +++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java @@ -255,6 +255,11 @@ final class DisplayDeviceInfo { public static final int DIFF_MODE_ID = 1 << 7; /** + * Diff result: The frame rate override list differs. + */ + public static final int DIFF_FRAME_RATE_OVERRIDE = 1 << 8; + + /** * Diff result: Catch-all for "everything changed" */ public static final int DIFF_EVERYTHING = 0XFFFFFFFF; @@ -523,6 +528,9 @@ final class DisplayDeviceInfo { if (modeId != other.modeId) { diff |= DIFF_MODE_ID; } + if (!Arrays.equals(frameRateOverrides, other.frameRateOverrides)) { + diff |= DIFF_FRAME_RATE_OVERRIDE; + } if (!Objects.equals(name, other.name) || !Objects.equals(uniqueId, other.uniqueId) || width != other.width @@ -546,7 +554,6 @@ final class DisplayDeviceInfo { || !Objects.equals(deviceProductInfo, other.deviceProductInfo) || ownerUid != other.ownerUid || !Objects.equals(ownerPackageName, other.ownerPackageName) - || !Arrays.equals(frameRateOverrides, other.frameRateOverrides) || !BrightnessSynchronizer.floatEquals(brightnessMinimum, other.brightnessMinimum) || !BrightnessSynchronizer.floatEquals(brightnessMaximum, other.brightnessMaximum) || !BrightnessSynchronizer.floatEquals(brightnessDefault, diff --git a/services/core/java/com/android/server/display/DisplayDeviceRepository.java b/services/core/java/com/android/server/display/DisplayDeviceRepository.java index 086f8a94d9b8..5f7bc4effa1b 100644 --- a/services/core/java/com/android/server/display/DisplayDeviceRepository.java +++ b/services/core/java/com/android/server/display/DisplayDeviceRepository.java @@ -27,6 +27,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.server.display.DisplayManagerService.SyncRoot; import com.android.server.display.utils.DebugUtils; +import java.util.Arrays; import java.util.ArrayList; import java.util.List; import java.util.function.Consumer; @@ -177,18 +178,22 @@ class DisplayDeviceRepository implements DisplayAdapter.Listener { "handleDisplayDeviceChanged"); } int diff = device.mDebugLastLoggedDeviceInfo.diff(info); - if (diff == DisplayDeviceInfo.DIFF_STATE) { + if (diff == 0) { + Slog.i(TAG, "Display device same: " + info); + } else if (diff == DisplayDeviceInfo.DIFF_STATE) { Slog.i(TAG, "Display device changed state: \"" + info.name + "\", " + Display.stateToString(info.state)); } else if (diff == DisplayDeviceInfo.DIFF_ROTATION) { Slog.i(TAG, "Display device rotated: \"" + info.name + "\", " + Surface.rotationToString(info.rotation)); - } else if (diff - == (DisplayDeviceInfo.DIFF_MODE_ID | DisplayDeviceInfo.DIFF_RENDER_TIMINGS)) { + } else if ((diff & + (DisplayDeviceInfo.DIFF_MODE_ID | DisplayDeviceInfo.DIFF_RENDER_TIMINGS + | DisplayDeviceInfo.DIFF_FRAME_RATE_OVERRIDE)) != 0) { Slog.i(TAG, "Display device changed render timings: \"" + info.name + "\", renderFrameRate=" + info.renderFrameRate + ", presentationDeadlineNanos=" + info.presentationDeadlineNanos - + ", appVsyncOffsetNanos=" + info.appVsyncOffsetNanos); + + ", appVsyncOffsetNanos=" + info.appVsyncOffsetNanos + + ", frameRateOverrides=" + Arrays.toString(info.frameRateOverrides)); } else if (diff == DisplayDeviceInfo.DIFF_COMMITTED_STATE) { if (DEBUG) { Slog.i(TAG, "Display device changed committed state: \"" + info.name diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 0e77040187e1..5a2610b00772 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -96,6 +96,7 @@ import android.hardware.display.DisplayManagerGlobal; import android.hardware.display.DisplayManagerInternal; import android.hardware.display.DisplayManagerInternal.DisplayGroupListener; import android.hardware.display.DisplayManagerInternal.DisplayTransactionListener; +import android.hardware.display.DisplayTopology; import android.hardware.display.DisplayViewport; import android.hardware.display.DisplayedContentSample; import android.hardware.display.DisplayedContentSamplingAttributes; @@ -118,6 +119,7 @@ import android.os.IBinder.DeathRecipient; import android.os.IThermalService; import android.os.Looper; import android.os.Message; +import android.os.PermissionEnforcer; import android.os.PowerManager; import android.os.Process; import android.os.RemoteException; @@ -4321,6 +4323,10 @@ public final class DisplayManagerService extends SystemService { @VisibleForTesting final class BinderService extends IDisplayManager.Stub { + BinderService() { + super(PermissionEnforcer.fromContext(getContext())); + } + /** * Returns information about the specified logical display. * @@ -5202,6 +5208,25 @@ public final class DisplayManagerService extends SystemService { } return ddc.getDefaultDozeBrightness(); } + + @EnforcePermission(MANAGE_DISPLAYS) + @Override // Binder call + public DisplayTopology getDisplayTopology() { + getDisplayTopology_enforcePermission(); + if (mDisplayTopologyCoordinator == null) { + return null; + } + return mDisplayTopologyCoordinator.getTopology(); + } + + @EnforcePermission(MANAGE_DISPLAYS) + @Override // Binder call + public void setDisplayTopology(DisplayTopology topology) { + setDisplayTopology_enforcePermission(); + if (mDisplayTopologyCoordinator != null) { + mDisplayTopologyCoordinator.setTopology(topology); + } + } } @VisibleForTesting diff --git a/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java b/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java index b101e5893b97..47226861545f 100644 --- a/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java +++ b/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java @@ -16,6 +16,7 @@ package com.android.server.display; +import android.hardware.display.DisplayTopology; import android.util.DisplayMetrics; import android.view.Display; import android.view.DisplayInfo; @@ -33,7 +34,7 @@ import java.util.function.BooleanSupplier; class DisplayTopologyCoordinator { @GuardedBy("mLock") - private final DisplayTopology mTopology; + private DisplayTopology mTopology; /** * Check if extended displays are enabled. If not, a topology is not needed. @@ -76,6 +77,21 @@ class DisplayTopologyCoordinator { } /** + * @return A deep copy of the topology. + */ + DisplayTopology getTopology() { + synchronized (mLock) { + return mTopology; + } + } + + void setTopology(DisplayTopology topology) { + synchronized (mLock) { + mTopology = topology; + } + } + + /** * Print the object's state and debug information into the given stream. * @param pw The stream to dump information to. */ @@ -108,6 +124,7 @@ class DisplayTopologyCoordinator { && info.displayGroupId == Display.DEFAULT_DISPLAY_GROUP; } + @VisibleForTesting static class Injector { DisplayTopology getTopology() { return new DisplayTopology(); 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 c0aa4cc6fa24..71f17d1f411e 100644 --- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java +++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java @@ -242,6 +242,11 @@ public class DisplayManagerFlags { Flags::autoBrightnessModeBedtimeWear ); + private final FlagState mEnablePluginManagerFlagState = new FlagState( + Flags.FLAG_ENABLE_PLUGIN_MANAGER, + Flags::enablePluginManager + ); + /** * @return {@code true} if 'port' is allowed in display layout configuration file. */ @@ -517,6 +522,10 @@ public class DisplayManagerFlags { return mAutoBrightnessModeBedtimeWearFlagState.isEnabled(); } + public boolean isPluginManagerEnabled() { + return mEnablePluginManagerFlagState.isEnabled(); + } + /** * dumps all flagstates * @param pw printWriter @@ -568,6 +577,7 @@ public class DisplayManagerFlags { pw.println(" " + mIsUserRefreshRateForExternalDisplayEnabled); pw.println(" " + mHasArrSupport); pw.println(" " + mAutoBrightnessModeBedtimeWearFlagState); + pw.println(" " + mEnablePluginManagerFlagState); } 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 a9bdccef2300..7850360c7dbf 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 @@ -446,3 +446,11 @@ flag { bug: "365163968" is_fixed_read_only: true } + +flag { + name: "enable_plugin_manager" + namespace: "display_manager" + description: "Flag to enable DisplayManager plugins" + bug: "354059797" + is_fixed_read_only: true +} diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java index 794eb8754820..0c04be10d06d 100644 --- a/services/core/java/com/android/server/dreams/DreamManagerService.java +++ b/services/core/java/com/android/server/dreams/DreamManagerService.java @@ -20,6 +20,7 @@ import static android.Manifest.permission.BIND_DREAM_SERVICE; import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT; import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; +import static android.service.dreams.Flags.cleanupDreamSettingsOnUninstall; import static android.service.dreams.Flags.dreamHandlesBeingObscured; import static com.android.server.wm.ActivityInterceptorCallback.DREAM_MANAGER_ORDERED_ID; @@ -353,28 +354,32 @@ public final class DreamManagerService extends SystemService { @Override public void onUserStarting(@NonNull TargetUser user) { super.onUserStarting(user); - mHandler.post(() -> { - final int userId = user.getUserIdentifier(); - if (!mPackageMonitors.contains(userId)) { - final PackageMonitor monitor = new PerUserPackageMonitor(); - monitor.register(mContext, UserHandle.of(userId), mHandler); - mPackageMonitors.put(userId, monitor); - } else { - Slog.w(TAG, "Package monitor already registered for " + userId); - } - }); + if (cleanupDreamSettingsOnUninstall()) { + mHandler.post(() -> { + final int userId = user.getUserIdentifier(); + if (!mPackageMonitors.contains(userId)) { + final PackageMonitor monitor = new PerUserPackageMonitor(); + monitor.register(mContext, UserHandle.of(userId), mHandler); + mPackageMonitors.put(userId, monitor); + } else { + Slog.w(TAG, "Package monitor already registered for " + userId); + } + }); + } } @Override public void onUserStopping(@NonNull TargetUser user) { super.onUserStopping(user); - mHandler.post(() -> { - final PackageMonitor monitor = mPackageMonitors.removeReturnOld( - user.getUserIdentifier()); - if (monitor != null) { - monitor.unregister(); - } - }); + if (cleanupDreamSettingsOnUninstall()) { + mHandler.post(() -> { + final PackageMonitor monitor = mPackageMonitors.removeReturnOld( + user.getUserIdentifier()); + if (monitor != null) { + monitor.unregister(); + } + }); + } } private void dumpInternal(PrintWriter pw) { @@ -715,15 +720,23 @@ public final class DreamManagerService extends SystemService { userId)); if (componentNames != null) { // Filter out any components in the removed package. - final ComponentName[] filteredComponents = Arrays.stream(componentNames).filter( - (componentName -> !TextUtils.equals(componentName.getPackageName(), - packageName))).toArray(ComponentName[]::new); + final ComponentName[] filteredComponents = + Arrays.stream(componentNames) + .filter((componentName -> !isSamePackage(packageName, componentName))) + .toArray(ComponentName[]::new); if (filteredComponents.length != componentNames.length) { setDreamComponentsForUser(userId, filteredComponents); } } } + private static boolean isSamePackage(String packageName, ComponentName componentName) { + if (packageName == null || componentName == null) { + return false; + } + return TextUtils.equals(componentName.getPackageName(), packageName); + } + private void setDreamComponentsForUser(int userId, ComponentName[] componentNames) { Settings.Secure.putStringForUser(mContext.getContentResolver(), Settings.Secure.SCREENSAVER_COMPONENTS, @@ -884,7 +897,10 @@ public final class DreamManagerService extends SystemService { } StringBuilder names = new StringBuilder(); for (ComponentName componentName : componentNames) { - if (names.length() > 0) { + if (componentName == null) { + continue; + } + if (!names.isEmpty()) { names.append(','); } names.append(componentName.flattenToString()); diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java index f2e2f653f929..5b4c0337862b 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecController.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java @@ -1001,7 +1001,7 @@ final class HdmiCecController { try { // Create an AIDL callback that can callback onHotplugEvent mHdmiConnection.setCallback(new HdmiConnectionCallbackAidl(callback)); - } catch (RemoteException e) { + } catch (RemoteException | NullPointerException e) { HdmiLogger.error("Couldn't initialise tv.hdmi callback : ", e); } } @@ -1134,7 +1134,7 @@ final class HdmiCecController { i++; } return hdmiPortInfo; - } catch (RemoteException e) { + } catch (RemoteException | NullPointerException e) { HdmiLogger.error("Failed to get port information : ", e); return null; } @@ -1144,7 +1144,7 @@ final class HdmiCecController { public boolean nativeIsConnected(int port) { try { return mHdmiConnection.isConnected(port); - } catch (RemoteException e) { + } catch (RemoteException | NullPointerException e) { HdmiLogger.error("Failed to get connection info : ", e); return false; } @@ -1158,7 +1158,7 @@ final class HdmiCecController { HdmiLogger.error( "Could not set HPD signal type for portId " + portId + " to " + signal + ". Error: ", sse.errorCode); - } catch (RemoteException e) { + } catch (RemoteException | NullPointerException e) { HdmiLogger.error( "Could not set HPD signal type for portId " + portId + " to " + signal + ". Exception: ", e); @@ -1169,7 +1169,7 @@ final class HdmiCecController { public int nativeGetHpdSignalType(int portId) { try { return mHdmiConnection.getHpdSignal(portId); - } catch (RemoteException e) { + } catch (RemoteException | NullPointerException e) { HdmiLogger.error( "Could not get HPD signal type for portId " + portId + ". Exception: ", e); return Constants.HDMI_HPD_TYPE_PHYSICAL; diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java index bf415a344f4c..7505c710f483 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java @@ -646,9 +646,9 @@ public class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { int address = message.getSource(); int type = message.getParams()[2]; - if (!ActiveSource.of(address, path).equals(getActiveSource())) { - HdmiLogger.debug("Check if a new device is connected to the active path"); - handleNewDeviceAtTheTailOfActivePath(path); + if (getActiveSource().logicalAddress != address && getActivePath() == path) { + HdmiLogger.debug("New logical address detected on the current active path."); + startRoutingControl(path, path, null); } startNewDeviceAction(ActiveSource.of(address, path), type); return Constants.HANDLED; diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index 132d6fa377eb..0c5069f81774 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -771,6 +771,14 @@ public class HdmiControlService extends SystemService { Slog.i(TAG, "Device does not support eARC."); } mHdmiCecNetwork = new HdmiCecNetwork(this, mCecController, mMhlController); + if (isTvDevice() && getWasCecDisabledOnStandbyByLowEnergyMode()) { + Slog.w(TAG, "Re-enable CEC on boot-up since it was disabled due to low energy " + + " mode."); + getHdmiCecConfig().setIntValue(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED, + HDMI_CEC_CONTROL_ENABLED); + setWasCecDisabledOnStandbyByLowEnergyMode(false); + setCecEnabled(HDMI_CEC_CONTROL_ENABLED); + } if (isCecControlEnabled()) { initializeCec(INITIATED_BY_BOOT_UP); } else { diff --git a/services/core/java/com/android/server/input/InputGestureManager.java b/services/core/java/com/android/server/input/InputGestureManager.java index e545dd507f28..6f3540221b63 100644 --- a/services/core/java/com/android/server/input/InputGestureManager.java +++ b/services/core/java/com/android/server/input/InputGestureManager.java @@ -215,6 +215,18 @@ final class InputGestureManager { systemShortcuts.add(createKeyGesture(KeyEvent.KEYCODE_T, KeyEvent.META_META_ON | KeyEvent.META_ALT_ON, KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_TALKBACK)); + systemShortcuts.add(createKeyGesture(KeyEvent.KEYCODE_MINUS, + KeyEvent.META_META_ON | KeyEvent.META_ALT_ON, + KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_OUT)); + systemShortcuts.add(createKeyGesture(KeyEvent.KEYCODE_EQUALS, + KeyEvent.META_META_ON | KeyEvent.META_ALT_ON, + KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_IN)); + systemShortcuts.add(createKeyGesture(KeyEvent.KEYCODE_M, + KeyEvent.META_META_ON | KeyEvent.META_ALT_ON, + KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION)); + systemShortcuts.add(createKeyGesture(KeyEvent.KEYCODE_S, + KeyEvent.META_META_ON | KeyEvent.META_ALT_ON, + KeyGestureEvent.KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK)); } if (keyboardA11yShortcutControl()) { if (InputSettings.isAccessibilityBounceKeysFeatureEnabled()) { diff --git a/services/core/java/com/android/server/input/KeyGestureController.java b/services/core/java/com/android/server/input/KeyGestureController.java index 155ffe805519..bb0b19009962 100644 --- a/services/core/java/com/android/server/input/KeyGestureController.java +++ b/services/core/java/com/android/server/input/KeyGestureController.java @@ -20,8 +20,7 @@ import static android.content.pm.PackageManager.FEATURE_LEANBACK; import static android.content.pm.PackageManager.FEATURE_WATCH; import static android.view.WindowManagerPolicyConstants.FLAG_INTERACTIVE; -import static com.android.hardware.input.Flags.useKeyGestureEventHandler; -import static com.android.hardware.input.Flags.useKeyGestureEventHandlerMultiPressGestures; +import static com.android.hardware.input.Flags.enableNew25q2Keycodes; import android.annotation.BinderThread; import android.annotation.MainThread; @@ -50,7 +49,6 @@ import android.os.Message; import android.os.Process; import android.os.RemoteException; import android.os.SystemClock; -import android.os.SystemProperties; import android.os.UserHandle; import android.provider.Settings; import android.util.IndentingPrintWriter; @@ -214,7 +212,7 @@ final class KeyGestureController { } private void initKeyCombinationRules() { - if (!useKeyGestureEventHandler() || !useKeyGestureEventHandlerMultiPressGestures()) { + if (!InputSettings.doesKeyGestureEventHandlerSupportMultiKeyGestures()) { return; } // TODO(b/358569822): Handle Power, Back key properly since key combination gesture is @@ -440,7 +438,8 @@ final class KeyGestureController { public boolean interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) { final boolean interactive = (policyFlags & FLAG_INTERACTIVE) != 0; - if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) { + if (InputSettings.doesKeyGestureEventHandlerSupportMultiKeyGestures() + && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) { return mKeyCombinationManager.interceptKey(event, interactive); } return false; @@ -456,15 +455,18 @@ final class KeyGestureController { final long keyConsumed = -1; final long keyNotConsumed = 0; - if (mKeyCombinationManager.isKeyConsumed(event)) { - return keyConsumed; - } + if (InputSettings.doesKeyGestureEventHandlerSupportMultiKeyGestures()) { + if (mKeyCombinationManager.isKeyConsumed(event)) { + return keyConsumed; + } - if ((flags & KeyEvent.FLAG_FALLBACK) == 0) { - final long now = SystemClock.uptimeMillis(); - final long interceptTimeout = mKeyCombinationManager.getKeyInterceptTimeout(keyCode); - if (now < interceptTimeout) { - return interceptTimeout - now; + if ((flags & KeyEvent.FLAG_FALLBACK) == 0) { + final long now = SystemClock.uptimeMillis(); + final long interceptTimeout = mKeyCombinationManager.getKeyInterceptTimeout( + keyCode); + if (now < interceptTimeout) { + return interceptTimeout - now; + } } } @@ -750,6 +752,28 @@ final class KeyGestureController { } } break; + case KeyEvent.KEYCODE_LOCK: + if (enableNew25q2Keycodes()) { + if (firstDown) { + handleKeyGesture(deviceId, new int[]{KeyEvent.KEYCODE_LOCK}, + /* modifierState = */0, + KeyGestureEvent.KEY_GESTURE_TYPE_LOCK_SCREEN, + KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId, focusedToken, + /* flags = */0, /* appLaunchData = */null); + } + } + return true; + case KeyEvent.KEYCODE_FULLSCREEN: + if (enableNew25q2Keycodes()) { + if (firstDown) { + handleKeyGesture(deviceId, new int[]{KeyEvent.KEYCODE_FULLSCREEN}, + /* modifierState = */0, + KeyGestureEvent.KEY_GESTURE_TYPE_MAXIMIZE_FREEFORM_WINDOW, + KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId, focusedToken, + /* flags = */0, /* appLaunchData = */null); + } + } + return true; case KeyEvent.KEYCODE_ASSIST: Slog.wtf(TAG, "KEYCODE_ASSIST should be handled in interceptKeyBeforeQueueing"); return true; @@ -764,6 +788,9 @@ final class KeyGestureController { Slog.wtf(TAG, "KEYCODE_STYLUS_BUTTON_* should be handled in" + " interceptKeyBeforeQueueing"); return true; + case KeyEvent.KEYCODE_DO_NOT_DISTURB: + // TODO(b/365920375): Implement 25Q2 keycode implementation in system + return true; } // Handle custom shortcuts diff --git a/services/core/java/com/android/server/input/KeyboardBacklightController.java b/services/core/java/com/android/server/input/KeyboardBacklightController.java index c3205afe14f2..0defd27eaae2 100644 --- a/services/core/java/com/android/server/input/KeyboardBacklightController.java +++ b/services/core/java/com/android/server/input/KeyboardBacklightController.java @@ -20,6 +20,7 @@ import android.animation.ValueAnimator; import android.annotation.BinderThread; import android.annotation.Nullable; import android.content.Context; +import android.content.res.Resources; import android.graphics.Color; import android.hardware.input.IKeyboardBacklightListener; import android.hardware.input.IKeyboardBacklightState; @@ -81,9 +82,6 @@ final class KeyboardBacklightController implements private static final String UEVENT_KEYBOARD_BACKLIGHT_TAG = "kbd_backlight"; @VisibleForTesting - static final long USER_INACTIVITY_THRESHOLD_MILLIS = Duration.ofSeconds(30).toMillis(); - - @VisibleForTesting static final int[] DEFAULT_BRIGHTNESS_VALUE_FOR_LEVEL = new int[DEFAULT_NUM_BRIGHTNESS_CHANGE_STEPS + 1]; @@ -112,6 +110,7 @@ final class KeyboardBacklightController implements private AmbientKeyboardBacklightController.AmbientKeyboardBacklightListener mAmbientListener; private int mAmbientBacklightValue = 0; + private final int mUserInactivityThresholdMs; static { // Fixed brightness levels to avoid issues when converting back and forth from the @@ -139,6 +138,9 @@ final class KeyboardBacklightController implements mAnimatorFactory = animatorFactory; mAmbientController = new AmbientKeyboardBacklightController(context, looper); mUEventManager = uEventManager; + Resources res = mContext.getResources(); + mUserInactivityThresholdMs = res.getInteger( + com.android.internal.R.integer.config_keyboardBacklightTimeoutMs); } @Override @@ -300,7 +302,7 @@ final class KeyboardBacklightController implements } mHandler.removeMessages(MSG_NOTIFY_USER_INACTIVITY); mHandler.sendEmptyMessageAtTime(MSG_NOTIFY_USER_INACTIVITY, - SystemClock.uptimeMillis() + USER_INACTIVITY_THRESHOLD_MILLIS); + SystemClock.uptimeMillis() + mUserInactivityThresholdMs); } private void handleUserInactivity() { diff --git a/services/core/java/com/android/server/input/KeyboardLedController.java b/services/core/java/com/android/server/input/KeyboardLedController.java index 5c404a2ae6e7..a2940d54fe4d 100644 --- a/services/core/java/com/android/server/input/KeyboardLedController.java +++ b/services/core/java/com/android/server/input/KeyboardLedController.java @@ -46,11 +46,13 @@ public final class KeyboardLedController implements InputManager.InputDeviceList private static final String TAG = KeyboardLedController.class.getSimpleName(); private static final int MSG_UPDATE_EXISTING_DEVICES = 1; private static final int MSG_UPDATE_MIC_MUTE_LED_STATE = 2; + private static final int MSG_UPDATE_AUDIO_MUTE_LED_STATE = 3; private final Context mContext; private final Handler mHandler; private final NativeInputManagerService mNative; private final SparseArray<InputDevice> mKeyboardsWithMicMuteLed = new SparseArray<>(); + private final SparseArray<InputDevice> mKeyboardsWithVolumeMuteLed = new SparseArray<>(); @NonNull private InputManager mInputManager; @NonNull @@ -66,6 +68,17 @@ public final class KeyboardLedController implements InputManager.InputDeviceList } }; + private BroadcastReceiver mVolumeMuteIntentReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1); + if (streamType == AudioManager.USE_DEFAULT_STREAM_TYPE) { + Message msg = Message.obtain(mHandler, MSG_UPDATE_AUDIO_MUTE_LED_STATE); + mHandler.sendMessage(msg); + } + } + }; + KeyboardLedController(Context context, Looper looper, NativeInputManagerService nativeService) { mContext = context; @@ -83,6 +96,9 @@ public final class KeyboardLedController implements InputManager.InputDeviceList case MSG_UPDATE_MIC_MUTE_LED_STATE: updateMicMuteLedState(); return true; + case MSG_UPDATE_AUDIO_MUTE_LED_STATE: + updateVolumeMuteLedState(); + return true; } return false; } @@ -105,6 +121,21 @@ public final class KeyboardLedController implements InputManager.InputDeviceList } } + private void updateVolumeMuteLedState() { + int color = mAudioManager.isStreamMute(AudioManager.USE_DEFAULT_STREAM_TYPE) + ? Color.WHITE : Color.TRANSPARENT; + for (int i = 0; i < mKeyboardsWithVolumeMuteLed.size(); i++) { + InputDevice device = mKeyboardsWithVolumeMuteLed.valueAt(i); + if (device != null) { + int deviceId = device.getId(); + Light light = getKeyboardVolumeMuteLight(device); + if (light != null) { + mNative.setLightColor(deviceId, light.getId(), color); + } + } + } + } + private Light getKeyboardMicMuteLight(InputDevice device) { for (Light light : device.getLightsManager().getLights()) { if (light.getType() == Light.LIGHT_TYPE_KEYBOARD_MIC_MUTE @@ -115,6 +146,16 @@ public final class KeyboardLedController implements InputManager.InputDeviceList return null; } + private Light getKeyboardVolumeMuteLight(InputDevice device) { + for (Light light : device.getLightsManager().getLights()) { + if (light.getType() == Light.LIGHT_TYPE_KEYBOARD_VOLUME_MUTE + && light.hasBrightnessControl()) { + return light; + } + } + return null; + } + /** Called when the system is ready for us to start third-party code. */ public void systemRunning() { mSensorPrivacyManager = Objects.requireNonNull( @@ -131,6 +172,12 @@ public final class KeyboardLedController implements InputManager.InputDeviceList new IntentFilter(AudioManager.ACTION_MICROPHONE_MUTE_CHANGED), null, mHandler); + mContext.registerReceiverAsUser( + mVolumeMuteIntentReceiver, + UserHandle.ALL, + new IntentFilter(AudioManager.STREAM_MUTE_CHANGED_ACTION), + null, + mHandler); } @Override @@ -141,6 +188,7 @@ public final class KeyboardLedController implements InputManager.InputDeviceList @Override public void onInputDeviceRemoved(int deviceId) { mKeyboardsWithMicMuteLed.remove(deviceId); + mKeyboardsWithVolumeMuteLed.remove(deviceId); } @Override @@ -154,6 +202,11 @@ public final class KeyboardLedController implements InputManager.InputDeviceList Message msg = Message.obtain(mHandler, MSG_UPDATE_MIC_MUTE_LED_STATE); mHandler.sendMessage(msg); } + if (getKeyboardVolumeMuteLight(inputDevice) != null) { + mKeyboardsWithVolumeMuteLed.put(deviceId, inputDevice); + Message msg = Message.obtain(mHandler, MSG_UPDATE_AUDIO_MUTE_LED_STATE); + mHandler.sendMessage(msg); + } } /** Dump the diagnostic information */ @@ -167,5 +220,14 @@ public final class KeyboardLedController implements InputManager.InputDeviceList + getKeyboardMicMuteLight(inputDevice).toString()); } ipw.decreaseIndent(); + ipw.println(TAG + ": " + mKeyboardsWithVolumeMuteLed.size() + + " keyboard volume mute lights"); + ipw.increaseIndent(); + for (int i = 0; i < mKeyboardsWithVolumeMuteLed.size(); i++) { + InputDevice inputDevice = mKeyboardsWithVolumeMuteLed.valueAt(i); + ipw.println(i + " " + inputDevice.getName() + ": " + + getKeyboardVolumeMuteLight(inputDevice).toString()); + } + ipw.decreaseIndent(); } } diff --git a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java index bb4ae96da53b..a132876b72a3 100644 --- a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java +++ b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java @@ -46,7 +46,6 @@ import android.util.Slog; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.server.LocalServices; -import com.android.server.integrity.model.RuleMetadata; import java.io.File; import java.nio.charset.StandardCharsets; diff --git a/services/core/java/com/android/server/integrity/model/BitInputStream.java b/services/core/java/com/android/server/integrity/model/BitInputStream.java deleted file mode 100644 index e7cc81eab26b..000000000000 --- a/services/core/java/com/android/server/integrity/model/BitInputStream.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.integrity.model; - -import java.io.IOException; -import java.io.InputStream; - -/** A wrapper class for reading a stream of bits. - * - * <p>Note: this class reads from underlying stream byte-by-byte. It is advised to apply buffering - * to underlying streams. - */ -public class BitInputStream { - - private long mBitsRead; - - private InputStream mInputStream; - - private byte mCurrentByte; - - public BitInputStream(InputStream inputStream) { - mInputStream = inputStream; - } - - /** - * Read the next number of bits from the stream. - * - * @param numOfBits The number of bits to read. - * @return The value read from the stream. - */ - public int getNext(int numOfBits) throws IOException { - int component = 0; - int count = 0; - - while (count++ < numOfBits) { - if (mBitsRead % 8 == 0) { - mCurrentByte = getNextByte(); - } - int offset = 7 - (int) (mBitsRead % 8); - - component <<= 1; - component |= (mCurrentByte >>> offset) & 1; - - mBitsRead++; - } - - return component; - } - - /** Check if there are bits left in the stream. */ - public boolean hasNext() throws IOException { - return mInputStream.available() > 0; - } - - private byte getNextByte() throws IOException { - return (byte) mInputStream.read(); - } -} diff --git a/services/core/java/com/android/server/integrity/model/BitOutputStream.java b/services/core/java/com/android/server/integrity/model/BitOutputStream.java deleted file mode 100644 index 14b35fd016eb..000000000000 --- a/services/core/java/com/android/server/integrity/model/BitOutputStream.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.integrity.model; - -import static com.android.server.integrity.model.ComponentBitSize.BYTE_BITS; - -import java.io.IOException; -import java.io.OutputStream; -import java.util.Arrays; - -/** A wrapper class for writing a stream of bits. */ -public class BitOutputStream { - - private static final int BUFFER_SIZE = 4 * 1024; - - private int mNextBitIndex; - - private final OutputStream mOutputStream; - private final byte[] mBuffer; - - public BitOutputStream(OutputStream outputStream) { - mBuffer = new byte[BUFFER_SIZE]; - mNextBitIndex = 0; - mOutputStream = outputStream; - } - - /** - * Set the next number of bits in the stream to value. - * - * @param numOfBits The number of bits used to represent the value. - * @param value The value to convert to bits. - */ - public void setNext(int numOfBits, int value) throws IOException { - if (numOfBits <= 0) { - return; - } - - // optional: we can do some clever size checking to "OR" an entire segment of bits instead - // of setting bits one by one, but it is probably not worth it. - int nextBitMask = 1 << (numOfBits - 1); - while (numOfBits-- > 0) { - setNext((value & nextBitMask) != 0); - nextBitMask >>>= 1; - } - } - - /** - * Set the next bit in the stream to value. - * - * @param value The value to set the bit to - */ - public void setNext(boolean value) throws IOException { - int byteToWrite = mNextBitIndex / BYTE_BITS; - if (byteToWrite == BUFFER_SIZE) { - mOutputStream.write(mBuffer); - reset(); - byteToWrite = 0; - } - if (value) { - mBuffer[byteToWrite] |= 1 << (BYTE_BITS - 1 - (mNextBitIndex % BYTE_BITS)); - } - mNextBitIndex++; - } - - /** Set the next bit in the stream to true. */ - public void setNext() throws IOException { - setNext(/* value= */ true); - } - - /** - * Flush the data written to the underlying {@link java.io.OutputStream}. Any unfinished bytes - * will be padded with 0. - */ - public void flush() throws IOException { - int endByte = mNextBitIndex / BYTE_BITS; - if (mNextBitIndex % BYTE_BITS != 0) { - // If next bit is not the first bit of a byte, then mNextBitIndex / BYTE_BITS would be - // the byte that includes already written bits. We need to increment it so this byte - // gets written. - endByte++; - } - mOutputStream.write(mBuffer, 0, endByte); - reset(); - } - - /** Reset this output stream to start state. */ - private void reset() { - mNextBitIndex = 0; - Arrays.fill(mBuffer, (byte) 0); - } -} diff --git a/services/core/java/com/android/server/integrity/model/ByteTrackedOutputStream.java b/services/core/java/com/android/server/integrity/model/ByteTrackedOutputStream.java deleted file mode 100644 index ceed054e4a53..000000000000 --- a/services/core/java/com/android/server/integrity/model/ByteTrackedOutputStream.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.integrity.model; - -import java.io.IOException; -import java.io.OutputStream; - -/** - * An output stream that tracks the total number written bytes since construction and allows - * querying this value any time during the execution. - * - * <p>This class is used for constructing the rule indexing. - */ -public class ByteTrackedOutputStream extends OutputStream { - - private int mWrittenBytesCount; - private final OutputStream mOutputStream; - - public ByteTrackedOutputStream(OutputStream outputStream) { - mWrittenBytesCount = 0; - mOutputStream = outputStream; - } - - @Override - public void write(int b) throws IOException { - mWrittenBytesCount++; - mOutputStream.write(b); - } - - /** - * Writes the given bytes into the output stream provided in constructor and updates the total - * number of written bytes. - */ - @Override - public void write(byte[] bytes) throws IOException { - write(bytes, 0, bytes.length); - } - - @Override - public void write(byte[] b, int off, int len) throws IOException { - mWrittenBytesCount += len; - mOutputStream.write(b, off, len); - } - - /** Returns the total number of bytes written into the output stream at the requested time. */ - public int getWrittenBytesCount() { - return mWrittenBytesCount; - } -} diff --git a/services/core/java/com/android/server/integrity/model/ComponentBitSize.java b/services/core/java/com/android/server/integrity/model/ComponentBitSize.java deleted file mode 100644 index 94e6708c3038..000000000000 --- a/services/core/java/com/android/server/integrity/model/ComponentBitSize.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.integrity.model; - -import android.content.integrity.Rule; - -/** - * A helper class containing information about the binary representation of different {@link Rule} - * components. - */ -public final class ComponentBitSize { - public static final int FORMAT_VERSION_BITS = 8; - - public static final int EFFECT_BITS = 3; - public static final int KEY_BITS = 4; - public static final int OPERATOR_BITS = 3; - public static final int CONNECTOR_BITS = 2; - public static final int SEPARATOR_BITS = 3; - public static final int VALUE_SIZE_BITS = 8; - public static final int IS_HASHED_BITS = 1; - - public static final int ATOMIC_FORMULA_START = 0; - public static final int COMPOUND_FORMULA_START = 1; - public static final int COMPOUND_FORMULA_END = 2; - public static final int INSTALLER_ALLOWED_BY_MANIFEST_START = 3; - - public static final int DEFAULT_FORMAT_VERSION = 1; - public static final int SIGNAL_BIT = 1; - - public static final int BYTE_BITS = 8; -} diff --git a/services/core/java/com/android/server/integrity/model/IntegrityCheckResult.java b/services/core/java/com/android/server/integrity/model/IntegrityCheckResult.java deleted file mode 100644 index b0647fc46473..000000000000 --- a/services/core/java/com/android/server/integrity/model/IntegrityCheckResult.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.integrity.model; - -import android.annotation.Nullable; -import android.content.integrity.Rule; - -import java.util.Collections; -import java.util.List; - -/** - * A class encapsulating the result from the evaluation engine after evaluating rules against app - * install metadata. - * - * <p>It contains the outcome effect (whether to allow or block the install), and the rule causing - * that effect. - */ -public final class IntegrityCheckResult { - - public enum Effect { - ALLOW, - DENY - } - - private final Effect mEffect; - private final List<Rule> mRuleList; - - private IntegrityCheckResult(Effect effect, @Nullable List<Rule> ruleList) { - this.mEffect = effect; - this.mRuleList = ruleList; - } - - public Effect getEffect() { - return mEffect; - } - - public List<Rule> getMatchedRules() { - return mRuleList; - } - - /** - * Create an ALLOW evaluation outcome. - * - * @return An evaluation outcome with ALLOW effect and no rule. - */ - public static IntegrityCheckResult allow() { - return new IntegrityCheckResult(Effect.ALLOW, Collections.emptyList()); - } - - /** - * Create an ALLOW evaluation outcome. - * - * @return An evaluation outcome with ALLOW effect and rule causing that effect. - */ - public static IntegrityCheckResult allow(List<Rule> ruleList) { - return new IntegrityCheckResult(Effect.ALLOW, ruleList); - } - - /** - * Create a DENY evaluation outcome. - * - * @param ruleList All valid rules that cause the DENY effect. - * @return An evaluation outcome with DENY effect and rule causing that effect. - */ - public static IntegrityCheckResult deny(List<Rule> ruleList) { - return new IntegrityCheckResult(Effect.DENY, ruleList); - } - - /** Returns true when the {@code mEffect} is caused by an app certificate mismatch. */ - public boolean isCausedByAppCertRule() { - return mRuleList.stream().anyMatch(rule -> rule.getFormula().isAppCertificateFormula()); - } - - /** Returns true when the {@code mEffect} is caused by an installer rule. */ - public boolean isCausedByInstallerRule() { - return mRuleList.stream().anyMatch(rule -> rule.getFormula().isInstallerFormula()); - } - -} diff --git a/services/core/java/com/android/server/integrity/model/RuleMetadata.java b/services/core/java/com/android/server/integrity/model/RuleMetadata.java deleted file mode 100644 index 6b582ae7b5f2..000000000000 --- a/services/core/java/com/android/server/integrity/model/RuleMetadata.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.integrity.model; - -import android.annotation.Nullable; - -/** Data class containing relevant metadata associated with a rule set. */ -public class RuleMetadata { - - private final String mRuleProvider; - private final String mVersion; - - public RuleMetadata(String ruleProvider, String version) { - mRuleProvider = ruleProvider; - mVersion = version; - } - - @Nullable - public String getRuleProvider() { - return mRuleProvider; - } - - @Nullable - public String getVersion() { - return mVersion; - } -} diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubService.java b/services/core/java/com/android/server/location/contexthub/ContextHubService.java index acc8f6634f5c..f611c57dab03 100644 --- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java +++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java @@ -35,6 +35,7 @@ import android.hardware.contexthub.MessageDeliveryStatus; import android.hardware.location.ContextHubInfo; import android.hardware.location.ContextHubMessage; import android.hardware.location.ContextHubTransaction; +import android.hardware.location.HubInfo; import android.hardware.location.IContextHubCallback; import android.hardware.location.IContextHubClient; import android.hardware.location.IContextHubClientCallback; @@ -57,6 +58,7 @@ import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; +import android.util.IndentingPrintWriter; import android.util.Log; import android.util.Pair; import android.util.proto.ProtoOutputStream; @@ -134,6 +136,9 @@ public class ContextHubService extends IContextHubService.Stub { private Map<Integer, ContextHubInfo> mContextHubIdToInfoMap; private List<String> mSupportedContextHubPerms; private List<ContextHubInfo> mContextHubInfoList; + + @Nullable private final HubInfoRegistry mHubInfoRegistry; + private final RemoteCallbackList<IContextHubCallback> mCallbacksList = new RemoteCallbackList<>(); @@ -309,10 +314,21 @@ public class ContextHubService extends IContextHubService.Stub { mContext = context; long startTimeNs = SystemClock.elapsedRealtimeNanos(); mContextHubWrapper = contextHubWrapper; + if (!initContextHubServiceState(startTimeNs)) { Log.e(TAG, "Failed to initialize the Context Hub Service"); + mHubInfoRegistry = null; return; } + + if (Flags.offloadApi()) { + mHubInfoRegistry = new HubInfoRegistry(mContextHubWrapper); + Log.i(TAG, "Enabling generic offload API"); + } else { + mHubInfoRegistry = null; + Log.i(TAG, "Disabling generic offload API"); + } + initDefaultClientMap(); initLocationSettingNotifications(); @@ -427,7 +443,7 @@ public class ContextHubService extends IContextHubService.Stub { Pair<List<ContextHubInfo>, List<String>> hubInfo; try { - hubInfo = mContextHubWrapper.getHubs(); + hubInfo = mContextHubWrapper.getContextHubs(); } catch (RemoteException e) { Log.e(TAG, "RemoteException while getting Context Hub info", e); hubInfo = new Pair<>(Collections.emptyList(), Collections.emptyList()); @@ -713,6 +729,16 @@ public class ContextHubService extends IContextHubService.Stub { return mContextHubInfoList; } + @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) + @Override + public List<HubInfo> getHubs() throws RemoteException { + super.getHubs_enforcePermission(); + if (mHubInfoRegistry == null) { + return Collections.emptyList(); + } + return mHubInfoRegistry.getHubs(); + } + /** * Creates an internal load transaction callback to be used for old API clients * @@ -1417,6 +1443,8 @@ public class ContextHubService extends IContextHubService.Stub { } } + IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); + pw = ipw; pw.println("Dumping ContextHub Service"); pw.println(""); @@ -1428,6 +1456,11 @@ public class ContextHubService extends IContextHubService.Stub { pw.println("Supported permissions: " + Arrays.toString(mSupportedContextHubPerms.toArray())); pw.println(""); + + if (mHubInfoRegistry != null) { + mHubInfoRegistry.dump(ipw); + } + pw.println("=================== NANOAPPS ===================="); // Dump nanoAppHash mNanoAppStateManager.foreachNanoAppInstanceInfo(pw::println); diff --git a/services/core/java/com/android/server/location/contexthub/HubInfoRegistry.java b/services/core/java/com/android/server/location/contexthub/HubInfoRegistry.java new file mode 100644 index 000000000000..68de9dbda2e1 --- /dev/null +++ b/services/core/java/com/android/server/location/contexthub/HubInfoRegistry.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.location.contexthub; + +import android.hardware.location.HubInfo; +import android.os.RemoteException; +import android.util.IndentingPrintWriter; +import android.util.Log; + +import java.util.Collections; +import java.util.List; + +class HubInfoRegistry { + private static final String TAG = "HubInfoRegistry"; + + private final IContextHubWrapper mContextHubWrapper; + + private final List<HubInfo> mHubsInfo; + + HubInfoRegistry(IContextHubWrapper contextHubWrapper) { + List<HubInfo> hubInfos; + mContextHubWrapper = contextHubWrapper; + try { + hubInfos = mContextHubWrapper.getHubs(); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException while getting Hub info", e); + hubInfos = Collections.emptyList(); + } + mHubsInfo = hubInfos; + } + + /** Retrieve the list of hubs available. */ + List<HubInfo> getHubs() { + return mHubsInfo; + } + + void dump(IndentingPrintWriter ipw) { + ipw.println(TAG); + + ipw.increaseIndent(); + for (HubInfo hubInfo : mHubsInfo) { + ipw.println(hubInfo); + } + ipw.decreaseIndent(); + } +} diff --git a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java index 5e9277ac0faf..6656a6fe9eb4 100644 --- a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java +++ b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java @@ -30,9 +30,11 @@ import android.hardware.contexthub.V1_2.HubAppInfo; import android.hardware.contexthub.V1_2.IContexthubCallback; import android.hardware.location.ContextHubInfo; import android.hardware.location.ContextHubTransaction; +import android.hardware.location.HubInfo; import android.hardware.location.NanoAppBinary; import android.hardware.location.NanoAppMessage; import android.hardware.location.NanoAppState; +import android.hardware.location.VendorHubInfo; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; @@ -52,13 +54,14 @@ import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.Set; -import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.Callable; +import java.util.concurrent.atomic.AtomicBoolean; /** * @hide */ public abstract class IContextHubWrapper { + private static final boolean DEBUG = false; private static final String TAG = "IContextHubWrapper"; /** @@ -217,10 +220,14 @@ public abstract class IContextHubWrapper { return proxy == null ? null : new ContextHubWrapperAidl(proxy); } - /** - * Calls the appropriate getHubs function depending on the HAL version. - */ - public abstract Pair<List<ContextHubInfo>, List<String>> getHubs() throws RemoteException; + /** Calls the appropriate getHubs function depending on the HAL version. */ + public abstract Pair<List<ContextHubInfo>, List<String>> getContextHubs() + throws RemoteException; + + /** Calls the appropriate getHubs function depending on the HAL version. */ + public List<HubInfo> getHubs() throws RemoteException { + return Collections.emptyList(); + } /** * @return True if this version of the Contexthub HAL supports Location setting notifications. @@ -556,7 +563,7 @@ public abstract class IContextHubWrapper { mIsTestModeEnabled.set(false); } - public Pair<List<ContextHubInfo>, List<String>> getHubs() throws RemoteException { + public Pair<List<ContextHubInfo>, List<String>> getContextHubs() throws RemoteException { android.hardware.contexthub.IContextHub hub = getHub(); if (hub == null) { return new Pair<List<ContextHubInfo>, List<String>>(new ArrayList<ContextHubInfo>(), @@ -574,6 +581,47 @@ public abstract class IContextHubWrapper { return new Pair(hubInfoList, new ArrayList<String>(supportedPermissions)); } + public List<HubInfo> getHubs() throws RemoteException { + android.hardware.contexthub.IContextHub hub = getHub(); + if (hub == null) { + return Collections.emptyList(); + } + + List<HubInfo> retVal = new ArrayList<>(); + final List<android.hardware.contexthub.HubInfo> halHubs = hub.getHubs(); + + for (android.hardware.contexthub.HubInfo halHub : halHubs) { + /* HAL -> API Type conversion */ + final HubInfo hubInfo; + switch (halHub.hubDetails.getTag()) { + case android.hardware.contexthub.HubInfo.HubDetails.contextHubInfo: + ContextHubInfo contextHubInfo = + new ContextHubInfo(halHub.hubDetails.getContextHubInfo()); + hubInfo = new HubInfo(halHub.hubId, contextHubInfo); + break; + case android.hardware.contexthub.HubInfo.HubDetails.vendorHubInfo: + VendorHubInfo vendorHubInfo = + new VendorHubInfo(halHub.hubDetails.getVendorHubInfo()); + hubInfo = new HubInfo(halHub.hubId, vendorHubInfo); + break; + default: + Log.w(TAG, "getHubs: invalid hub: " + halHub); + // Invalid + continue; + } + + if (DEBUG) { + Log.i(TAG, "getHubs: hubInfo=" + hubInfo); + } + retVal.add(hubInfo); + } + + if (DEBUG) { + Log.i(TAG, "getHubs: total count=" + retVal.size()); + } + return retVal; + } + public boolean supportsLocationSettingNotifications() { return true; } @@ -1061,7 +1109,7 @@ public abstract class IContextHubWrapper { mHub = hub; } - public Pair<List<ContextHubInfo>, List<String>> getHubs() throws RemoteException { + public Pair<List<ContextHubInfo>, List<String>> getContextHubs() throws RemoteException { ArrayList<ContextHubInfo> hubInfoList = new ArrayList<>(); for (ContextHub hub : mHub.getHubs()) { hubInfoList.add(new ContextHubInfo(hub)); @@ -1106,7 +1154,7 @@ public abstract class IContextHubWrapper { mHub = hub; } - public Pair<List<ContextHubInfo>, List<String>> getHubs() throws RemoteException { + public Pair<List<ContextHubInfo>, List<String>> getContextHubs() throws RemoteException { ArrayList<ContextHubInfo> hubInfoList = new ArrayList<>(); for (ContextHub hub : mHub.getHubs()) { hubInfoList.add(new ContextHubInfo(hub)); @@ -1170,7 +1218,7 @@ public abstract class IContextHubWrapper { mHubInfo = new Pair(hubInfoList, supportedPermissions); } - public Pair<List<ContextHubInfo>, List<String>> getHubs() throws RemoteException { + public Pair<List<ContextHubInfo>, List<String>> getContextHubs() throws RemoteException { mHub.getHubs_1_2(this); return mHubInfo; } diff --git a/services/core/java/com/android/server/media/quality/MediaQualityDbHelper.java b/services/core/java/com/android/server/media/quality/MediaQualityDbHelper.java new file mode 100644 index 000000000000..04f6216694a9 --- /dev/null +++ b/services/core/java/com/android/server/media/quality/MediaQualityDbHelper.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.media.quality; + +import android.content.Context; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; +import android.media.quality.MediaQualityContract.BaseParameters; + +public class MediaQualityDbHelper extends SQLiteOpenHelper { + + private static final String TAG = "MediaQualityDbHelper"; + + static final int DATABASE_VERSION_1 = 1; + private static final String DATABASE_NAME = "media_quality.db"; + public static final String PICTURE_QUALITY_TABLE_NAME = "picture_quality"; + public static final String SOUND_QUALITY_TABLE_NAME = "sound_quality"; + public static final String SETTINGS = "settings"; + + MediaQualityDbHelper(Context context) { + super(context, DATABASE_NAME, null, getDbVersion()); + } + + private static int getDbVersion() { + return DATABASE_VERSION_1; + } + + @Override + public void onCreate(SQLiteDatabase db) { + db.execSQL(getTableCreateStatement(PICTURE_QUALITY_TABLE_NAME)); + db.execSQL(getTableCreateStatement(SOUND_QUALITY_TABLE_NAME)); + } + + private String getTableCreateStatement(String tableName) { + return + "CREATE TABLE " + tableName + "(" + + BaseParameters.PARAMETER_ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + + BaseParameters.PARAMETER_TYPE + " INTEGER," + + BaseParameters.PARAMETER_NAME + " STRING," + + BaseParameters.PARAMETER_PACKAGE + " STRING," + + BaseParameters.PARAMETER_INPUT_ID + " STRING," + + SETTINGS + " TEXT)"; + } + + @Override + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + // to do + } + +} 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 a45ea1d8369d..84413d5710d0 100644 --- a/services/core/java/com/android/server/media/quality/MediaQualityService.java +++ b/services/core/java/com/android/server/media/quality/MediaQualityService.java @@ -16,20 +16,31 @@ package com.android.server.media.quality; +import android.content.ContentValues; import android.content.Context; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; import android.media.quality.AmbientBacklightSettings; 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.PictureQuality; import android.media.quality.ParamCapability; import android.media.quality.PictureProfile; import android.media.quality.SoundProfile; +import android.os.Bundle; +import android.util.Log; import com.android.server.SystemService; +import org.json.JSONException; +import org.json.JSONObject; + import java.util.ArrayList; +import java.util.Iterator; import java.util.List; +import java.util.Locale; /** * This service manage picture profile and sound profile for TV setting. Also communicates with the @@ -40,10 +51,14 @@ public class MediaQualityService extends SystemService { private static final boolean DEBUG = false; private static final String TAG = "MediaQualityService"; private final Context mContext; + private final MediaQualityDbHelper mMediaQualityDbHelper; public MediaQualityService(Context context) { super(context); mContext = context; + mMediaQualityDbHelper = new MediaQualityDbHelper(mContext); + mMediaQualityDbHelper.setWriteAheadLoggingEnabled(true); + mMediaQualityDbHelper.setIdleConnectionTimeout(30); } @Override @@ -53,23 +68,83 @@ public class MediaQualityService extends SystemService { // TODO: Add additional APIs. b/373951081 private final class BinderService extends IMediaQualityManager.Stub { + @Override public PictureProfile createPictureProfile(PictureProfile pp) { - // TODO: implement - return pp; + SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase(); + + ContentValues values = new ContentValues(); + values.put(PictureQuality.PARAMETER_TYPE, pp.getProfileType()); + values.put(PictureQuality.PARAMETER_NAME, pp.getName()); + values.put(PictureQuality.PARAMETER_PACKAGE, pp.getPackageName()); + values.put(PictureQuality.PARAMETER_INPUT_ID, pp.getInputId()); + values.put(mMediaQualityDbHelper.SETTINGS, bundleToJson(pp.getParameters())); + + // id is auto-generated by SQLite upon successful insertion of row + long id = db.insert(mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME, null, values); + return new PictureProfile.Builder(pp).setProfileId(Long.toString(id)).build(); } + @Override - public void updatePictureProfile(long id, PictureProfile pp) { + public void updatePictureProfile(String id, PictureProfile pp) { // TODO: implement } @Override - public void removePictureProfile(long id) { + public void removePictureProfile(String id) { // TODO: implement } + @Override - public PictureProfile getPictureProfileById(long id) { + public PictureProfile getPictureProfile(int type, String name) { return null; } + + private String bundleToJson(Bundle bundle) { + JSONObject jsonObject = new JSONObject(); + if (bundle == null) { + return jsonObject.toString(); + } + for (String key : bundle.keySet()) { + try { + jsonObject.put(key, bundle.getString(key)); + } catch (JSONException e) { + Log.e(TAG, "Unable to serialize ", e); + } + } + return jsonObject.toString(); + } + + private Bundle jsonToBundle(String jsonString) { + JSONObject jsonObject = null; + Bundle bundle = new Bundle(); + + try { + jsonObject = new JSONObject(jsonString); + + Iterator<String> keys = jsonObject.keys(); + while (keys.hasNext()) { + String key = keys.next(); + Object value = jsonObject.get(key); + + if (value instanceof String) { + bundle.putString(key, (String) value); + } else if (value instanceof Integer) { + bundle.putInt(key, (Integer) value); + } else if (value instanceof Boolean) { + bundle.putBoolean(key, (Boolean) value); + } else if (value instanceof Double) { + bundle.putDouble(key, (Double) value); + } else if (value instanceof Long) { + bundle.putLong(key, (Long) value); + } + } + } catch (JSONException e) { + throw new RuntimeException(e); + } + + return bundle; + } + @Override public List<PictureProfile> getPictureProfilesByPackage(String packageName) { return new ArrayList<>(); @@ -79,7 +154,7 @@ public class MediaQualityService extends SystemService { return new ArrayList<>(); } @Override - public List<PictureProfile> getAllPictureProfiles() { + public List<String> getPictureProfilePackageNames() { return new ArrayList<>(); } @@ -89,15 +164,15 @@ public class MediaQualityService extends SystemService { return pp; } @Override - public void updateSoundProfile(long id, SoundProfile pp) { + public void updateSoundProfile(String id, SoundProfile pp) { // TODO: implement } @Override - public void removeSoundProfile(long id) { + public void removeSoundProfile(String id) { // TODO: implement } @Override - public SoundProfile getSoundProfileById(long id) { + public SoundProfile getSoundProfileById(String id) { return null; } @Override @@ -109,7 +184,7 @@ public class MediaQualityService extends SystemService { return new ArrayList<>(); } @Override - public List<SoundProfile> getAllSoundProfiles() { + public List<String> getSoundProfilePackageNames() { return new ArrayList<>(); } @@ -138,6 +213,14 @@ public class MediaQualityService extends SystemService { return new ArrayList<>(); } + @Override + public List<String> getPictureProfileAllowList() { + return new ArrayList<>(); + } + + @Override + public void setPictureProfileAllowList(List<String> packages) { + } @Override public boolean isSupported() { diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index 2a3be1e119bf..7de2815eba6b 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -513,12 +513,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private boolean mLoadedRestrictBackground; /** - * Whether or not network for apps in proc-states greater than - * {@link NetworkPolicyManager#BACKGROUND_THRESHOLD_STATE} is always blocked. - */ - private boolean mBackgroundNetworkRestricted; - - /** * Whether or not metered firewall chains should be used for uid policy controlling access to * metered networks. */ @@ -1117,14 +1111,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { writePolicyAL(); } - // The flag is boot-stable. - mBackgroundNetworkRestricted = Flags.networkBlockedForTopSleepingAndAbove(); - if (mBackgroundNetworkRestricted) { - // Firewall rules and UidBlockedState will get updated in - // updateRulesForGlobalChangeAL below. - enableFirewallChainUL(FIREWALL_CHAIN_BACKGROUND, true); - } - + enableFirewallChainUL(FIREWALL_CHAIN_BACKGROUND, true); setRestrictBackgroundUL(mLoadedRestrictBackground, "init_service"); updateRulesForGlobalChangeAL(false); updateNotificationsNL(); @@ -1135,11 +1122,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final int changes = ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE | ActivityManager.UID_OBSERVER_CAPABILITY; - - final int cutpoint = mBackgroundNetworkRestricted ? PROCESS_STATE_UNKNOWN - : NetworkPolicyManager.FOREGROUND_THRESHOLD_STATE; mActivityManagerInternal.registerNetworkPolicyUidObserver(mUidObserver, changes, - cutpoint, "android"); + PROCESS_STATE_UNKNOWN, "android"); mNetworkManager.registerObserver(mAlertObserver); } catch (RemoteException e) { // ignored; both services live in system_server @@ -1280,21 +1264,19 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // different chains may change. return true; } - if (mBackgroundNetworkRestricted) { - if ((previousProcState >= BACKGROUND_THRESHOLD_STATE) + if ((previousProcState >= BACKGROUND_THRESHOLD_STATE) != (newProcState >= BACKGROUND_THRESHOLD_STATE)) { - // Proc-state change crossed BACKGROUND_THRESHOLD_STATE: The network rules will - // need to be re-evaluated for the background chain. - return true; - } - if (mUseDifferentDelaysForBackgroundChain - && newProcState >= BACKGROUND_THRESHOLD_STATE - && getBackgroundTransitioningDelay(newProcState) - < getBackgroundTransitioningDelay(previousProcState)) { - // The old and new proc-state both are in the blocked state but the background - // transition delay is reduced, so we may have to update the rules sooner. - return true; - } + // Proc-state change crossed BACKGROUND_THRESHOLD_STATE: The network rules will + // need to be re-evaluated for the background chain. + return true; + } + if (mUseDifferentDelaysForBackgroundChain + && newProcState >= BACKGROUND_THRESHOLD_STATE + && getBackgroundTransitioningDelay(newProcState) + < getBackgroundTransitioningDelay(previousProcState)) { + // The old and new proc-state both are in the blocked state but the background + // transition delay is reduced, so we may have to update the rules sooner. + return true; } final int networkCapabilities = PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK | PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK; @@ -1367,9 +1349,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // on background handler thread, and POWER_SAVE_WHITELIST_CHANGED is protected synchronized (mUidRulesFirstLock) { updatePowerSaveAllowlistUL(); - if (mBackgroundNetworkRestricted) { - updateRulesForBackgroundChainUL(); - } + updateRulesForBackgroundChainUL(); updateRulesForRestrictPowerUL(); updateRulesForAppIdleUL(); } @@ -4100,8 +4080,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { fout.println(); fout.println("Flags:"); - fout.println(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE + ": " - + mBackgroundNetworkRestricted); fout.println(Flags.FLAG_USE_METERED_FIREWALL_CHAINS + ": " + mUseMeteredFirewallChains); fout.println(Flags.FLAG_USE_DIFFERENT_DELAYS_FOR_BACKGROUND_CHAIN + ": " @@ -4251,35 +4229,33 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { fout.decreaseIndent(); } - if (mBackgroundNetworkRestricted) { + fout.println(); + if (mUseDifferentDelaysForBackgroundChain) { + fout.print("Background restrictions short delay: "); + TimeUtils.formatDuration(mBackgroundRestrictionShortDelayMs, fout); fout.println(); - if (mUseDifferentDelaysForBackgroundChain) { - fout.print("Background restrictions short delay: "); - TimeUtils.formatDuration(mBackgroundRestrictionShortDelayMs, fout); - fout.println(); - fout.print("Background restrictions long delay: "); - TimeUtils.formatDuration(mBackgroundRestrictionLongDelayMs, fout); - fout.println(); - } + fout.print("Background restrictions long delay: "); + TimeUtils.formatDuration(mBackgroundRestrictionLongDelayMs, fout); + fout.println(); + } - size = mBackgroundTransitioningUids.size(); - if (size > 0) { - final long nowUptime = SystemClock.uptimeMillis(); - fout.println("Uids transitioning to background:"); - fout.increaseIndent(); - for (int i = 0; i < size; i++) { - fout.print("UID="); - fout.print(mBackgroundTransitioningUids.keyAt(i)); - fout.print(", "); - TimeUtils.formatDuration(mBackgroundTransitioningUids.valueAt(i), - nowUptime, fout); - fout.println(); - } - fout.decreaseIndent(); + size = mBackgroundTransitioningUids.size(); + if (size > 0) { + final long nowUptime = SystemClock.uptimeMillis(); + fout.println("Uids transitioning to background:"); + fout.increaseIndent(); + for (int i = 0; i < size; i++) { + fout.print("UID="); + fout.print(mBackgroundTransitioningUids.keyAt(i)); + fout.print(", "); + TimeUtils.formatDuration(mBackgroundTransitioningUids.valueAt(i), + nowUptime, fout); + fout.println(); } - fout.println(); + fout.decreaseIndent(); } + fout.println(); final SparseBooleanArray knownUids = new SparseBooleanArray(); collectKeys(mUidState, knownUids); @@ -4465,51 +4441,49 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } updatePowerRestrictionRules = true; } - if (mBackgroundNetworkRestricted) { - final boolean wasAllowed = isProcStateAllowedNetworkWhileBackground( - oldUidState); - final boolean isAllowed = isProcStateAllowedNetworkWhileBackground(newUidState); - if (!wasAllowed && isAllowed) { - mBackgroundTransitioningUids.delete(uid); - updateRuleForBackgroundUL(uid); - updatePowerRestrictionRules = true; - } else if (!isAllowed) { - final int transitionIdx = mBackgroundTransitioningUids.indexOfKey(uid); - final long completionTimeMs = SystemClock.uptimeMillis() - + getBackgroundTransitioningDelay(procState); - boolean completionTimeUpdated = false; - if (wasAllowed) { - // Rules need to transition from allowed to blocked after the respective - // delay. - if (transitionIdx < 0) { - // This is just a defensive check in case the upstream code ever - // makes multiple calls for the same process state change. - mBackgroundTransitioningUids.put(uid, completionTimeMs); - completionTimeUpdated = true; - } - } else if (mUseDifferentDelaysForBackgroundChain) { - // wasAllowed was false, but the transition delay may have reduced. - // Currently, this can happen when the uid transitions from - // LAST_ACTIVITY to CACHED_ACTIVITY, for example. - if (transitionIdx >= 0 - && completionTimeMs < mBackgroundTransitioningUids.valueAt( - transitionIdx)) { - mBackgroundTransitioningUids.setValueAt(transitionIdx, - completionTimeMs); - completionTimeUpdated = true; - } + final boolean wasAllowed = isProcStateAllowedNetworkWhileBackground( + oldUidState); + final boolean isAllowed = isProcStateAllowedNetworkWhileBackground(newUidState); + if (!wasAllowed && isAllowed) { + mBackgroundTransitioningUids.delete(uid); + updateRuleForBackgroundUL(uid); + updatePowerRestrictionRules = true; + } else if (!isAllowed) { + final int transitionIdx = mBackgroundTransitioningUids.indexOfKey(uid); + final long completionTimeMs = SystemClock.uptimeMillis() + + getBackgroundTransitioningDelay(procState); + boolean completionTimeUpdated = false; + if (wasAllowed) { + // Rules need to transition from allowed to blocked after the respective + // delay. + if (transitionIdx < 0) { + // This is just a defensive check in case the upstream code ever + // makes multiple calls for the same process state change. + mBackgroundTransitioningUids.put(uid, completionTimeMs); + completionTimeUpdated = true; } - if (completionTimeUpdated - && completionTimeMs < mNextProcessBackgroundUidsTime) { - // Many uids may be in this "transitioning" state at the same time, - // so we always keep one message to process transition completion at - // the earliest time. - mHandler.removeMessages(MSG_PROCESS_BACKGROUND_TRANSITIONING_UIDS); - mHandler.sendEmptyMessageAtTime( - MSG_PROCESS_BACKGROUND_TRANSITIONING_UIDS, completionTimeMs); - mNextProcessBackgroundUidsTime = completionTimeMs; + } else if (mUseDifferentDelaysForBackgroundChain) { + // wasAllowed was false, but the transition delay may have reduced. + // Currently, this can happen when the uid transitions from + // LAST_ACTIVITY to CACHED_ACTIVITY, for example. + if (transitionIdx >= 0 + && completionTimeMs < mBackgroundTransitioningUids.valueAt( + transitionIdx)) { + mBackgroundTransitioningUids.setValueAt(transitionIdx, + completionTimeMs); + completionTimeUpdated = true; } } + if (completionTimeUpdated + && completionTimeMs < mNextProcessBackgroundUidsTime) { + // Many uids may be in this "transitioning" state at the same time, + // so we always keep one message to process transition completion at + // the earliest time. + mHandler.removeMessages(MSG_PROCESS_BACKGROUND_TRANSITIONING_UIDS); + mHandler.sendEmptyMessageAtTime( + MSG_PROCESS_BACKGROUND_TRANSITIONING_UIDS, completionTimeMs); + mNextProcessBackgroundUidsTime = completionTimeMs; + } } if (mLowPowerStandbyActive) { boolean allowedInLpsChanged = @@ -4545,12 +4519,10 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { if (mRestrictPower) { updateRuleForRestrictPowerUL(uid); } - if (mBackgroundNetworkRestricted) { - // Uid is no longer running, there is no point in any grace period of network - // access during transitions to lower importance proc-states. - mBackgroundTransitioningUids.delete(uid); - updateRuleForBackgroundUL(uid); - } + // Uid is no longer running, there is no point in any grace period of network + // access during transitions to lower importance proc-states. + mBackgroundTransitioningUids.delete(uid); + updateRuleForBackgroundUL(uid); updateRulesForPowerRestrictionsUL(uid); if (mLowPowerStandbyActive) { updateRuleForLowPowerStandbyUL(uid); @@ -5021,9 +4993,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { "updateRulesForGlobalChangeAL: " + (restrictedNetworksChanged ? "R" : "-")); } try { - if (mBackgroundNetworkRestricted) { - updateRulesForBackgroundChainUL(); - } + updateRulesForBackgroundChainUL(); updateRulesForAppIdleUL(); updateRulesForRestrictPowerUL(); updateRulesForRestrictBackgroundUL(); @@ -5183,9 +5153,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { updateRuleForAppIdleUL(uid, PROCESS_STATE_UNKNOWN); updateRuleForDeviceIdleUL(uid); updateRuleForRestrictPowerUL(uid); - if (mBackgroundNetworkRestricted) { - updateRuleForBackgroundUL(uid); - } + updateRuleForBackgroundUL(uid); // Update internal rules. updateRulesForPowerRestrictionsUL(uid); } @@ -5358,9 +5326,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { updateRuleForDeviceIdleUL(uid); updateRuleForAppIdleUL(uid, PROCESS_STATE_UNKNOWN); updateRuleForRestrictPowerUL(uid); - if (mBackgroundNetworkRestricted) { - updateRuleForBackgroundUL(uid); - } + updateRuleForBackgroundUL(uid); // If the uid has the necessary permissions, then it should be added to the restricted mode // firewall allowlist. @@ -5611,7 +5577,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { newBlockedReasons |= (mLowPowerStandbyActive ? BLOCKED_REASON_LOW_POWER_STANDBY : 0); newBlockedReasons |= (isUidIdle ? BLOCKED_REASON_APP_STANDBY : 0); newBlockedReasons |= (uidBlockedState.blockedReasons & BLOCKED_REASON_RESTRICTED_MODE); - newBlockedReasons |= mBackgroundNetworkRestricted ? BLOCKED_REASON_APP_BACKGROUND : 0; + newBlockedReasons |= BLOCKED_REASON_APP_BACKGROUND; newAllowedReasons |= (isSystem(uid) ? ALLOWED_REASON_SYSTEM : 0); newAllowedReasons |= (isForeground ? ALLOWED_REASON_FOREGROUND : 0); @@ -5624,8 +5590,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { & ALLOWED_REASON_RESTRICTED_MODE_PERMISSIONS); newAllowedReasons |= (isAllowlistedFromLowPowerStandbyUL(uid)) ? ALLOWED_REASON_LOW_POWER_STANDBY_ALLOWLIST : 0; - newAllowedReasons |= (mBackgroundNetworkRestricted - && isUidExemptFromBackgroundRestrictions(uid)) + newAllowedReasons |= isUidExemptFromBackgroundRestrictions(uid) ? ALLOWED_REASON_NOT_IN_BACKGROUND : 0; uidBlockedState.blockedReasons = (uidBlockedState.blockedReasons diff --git a/services/core/java/com/android/server/net/flags.aconfig b/services/core/java/com/android/server/net/flags.aconfig index 7f04e665567e..3c0ff6115fcd 100644 --- a/services/core/java/com/android/server/net/flags.aconfig +++ b/services/core/java/com/android/server/net/flags.aconfig @@ -2,13 +2,6 @@ package: "com.android.server.net" container: "system" flag { - name: "network_blocked_for_top_sleeping_and_above" - namespace: "backstage_power" - description: "Block network access for apps in a low importance background state" - bug: "304347838" -} - -flag { name: "use_metered_firewall_chains" namespace: "backstage_power" description: "Use metered firewall chains to control access to metered networks" diff --git a/services/core/java/com/android/server/notification/GroupHelper.java b/services/core/java/com/android/server/notification/GroupHelper.java index 5febd5c07e3a..5914dbe44b0b 100644 --- a/services/core/java/com/android/server/notification/GroupHelper.java +++ b/services/core/java/com/android/server/notification/GroupHelper.java @@ -788,6 +788,20 @@ public class GroupHelper { return; } + // Check if summary & child notifications are not part of the same section/bundle + // Needs a check here if notification was bundled while enqueued + if (notificationRegroupOnClassification() + && android.service.notification.Flags.notificationClassification()) { + if (isGroupChildBundled(record, summaryByGroupKey)) { + if (DEBUG) { + Slog.v(TAG, "isGroupChildInDifferentBundleThanSummary: " + record); + } + moveNotificationsToNewSection(record.getUserId(), pkgName, + List.of(new NotificationMoveOp(record, null, fullAggregateGroupKey))); + return; + } + } + // scenario 3: sparse/singleton groups if (Flags.notificationForceGroupSingletons()) { try { @@ -800,6 +814,27 @@ public class GroupHelper { } } + private static boolean isGroupChildBundled(final NotificationRecord record, + final Map<String, NotificationRecord> summaryByGroupKey) { + final StatusBarNotification sbn = record.getSbn(); + final String groupKey = record.getSbn().getGroupKey(); + + if (!sbn.isAppGroup()) { + return false; + } + + if (record.getNotification().isGroupSummary()) { + return false; + } + + final NotificationRecord summary = summaryByGroupKey.get(groupKey); + if (summary == null) { + return false; + } + + return NotificationChannel.SYSTEM_RESERVED_IDS.contains(record.getChannel().getId()); + } + /** * Called when a notification is removed, so that this helper can adjust the aggregate groups: * - Removes the autogroup summary of the notification's section @@ -1598,7 +1633,7 @@ public class GroupHelper { final int mSummaryId; private final Predicate<NotificationRecord> mSectionChecker; - public NotificationSectioner(String name, int summaryId, + private NotificationSectioner(String name, int summaryId, Predicate<NotificationRecord> sectionChecker) { mName = name; mSummaryId = summaryId; diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java index 93482e769a2b..b0ef80793cd7 100644 --- a/services/core/java/com/android/server/notification/ManagedServices.java +++ b/services/core/java/com/android/server/notification/ManagedServices.java @@ -75,9 +75,7 @@ import com.android.internal.util.XmlUtils; import com.android.internal.util.function.TriPredicate; import com.android.modules.utils.TypedXmlPullParser; import com.android.modules.utils.TypedXmlSerializer; -import com.android.server.LocalServices; import com.android.server.notification.NotificationManagerService.DumpFilter; -import com.android.server.pm.UserManagerInternal; import com.android.server.utils.TimingsTraceAndSlog; import org.xmlpull.v1.XmlPullParser; @@ -136,7 +134,6 @@ abstract public class ManagedServices { private final UserProfiles mUserProfiles; protected final IPackageManager mPm; protected final UserManager mUm; - private final UserManagerInternal mUserManagerInternal; private final Config mConfig; private final Handler mHandler = new Handler(Looper.getMainLooper()); @@ -198,7 +195,6 @@ abstract public class ManagedServices { mConfig = getConfig(); mApprovalLevel = APPROVAL_BY_COMPONENT; mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE); - mUserManagerInternal = LocalServices.getService(UserManagerInternal.class); } abstract protected Config getConfig(); @@ -1389,14 +1385,9 @@ abstract public class ManagedServices { @GuardedBy("mMutex") protected void populateComponentsToBind(SparseArray<Set<ComponentName>> componentsToBind, final IntArray activeUsers, - SparseArray<ArraySet<ComponentName>> approvedComponentsByUser, - boolean isVisibleBackgroundUser) { - // When it is a visible background user in Automotive MUMD environment, - // don't clear mEnabledServicesForCurrentProfile and mEnabledServicesPackageNames. - if (!isVisibleBackgroundUser) { - mEnabledServicesForCurrentProfiles.clear(); - mEnabledServicesPackageNames.clear(); - } + SparseArray<ArraySet<ComponentName>> approvedComponentsByUser) { + mEnabledServicesForCurrentProfiles.clear(); + mEnabledServicesPackageNames.clear(); final int nUserIds = activeUsers.size(); for (int i = 0; i < nUserIds; ++i) { @@ -1417,12 +1408,7 @@ abstract public class ManagedServices { } componentsToBind.put(userId, add); - // When it is a visible background user in Automotive MUMD environment, - // skip adding items to mEnabledServicesForCurrentProfile - // and mEnabledServicesPackageNames. - if (isVisibleBackgroundUser) { - continue; - } + mEnabledServicesForCurrentProfiles.addAll(userComponents); for (int j = 0; j < userComponents.size(); j++) { @@ -1470,10 +1456,7 @@ abstract public class ManagedServices { IntArray userIds = mUserProfiles.getCurrentProfileIds(); boolean rebindAllCurrentUsers = mUserProfiles.isProfileUser(userToRebind, mContext) && allowRebindForParentUser(); - boolean isVisibleBackgroundUser = false; if (userToRebind != USER_ALL && !rebindAllCurrentUsers) { - isVisibleBackgroundUser = - mUserManagerInternal.isVisibleBackgroundFullUser(userToRebind); userIds = new IntArray(1); userIds.add(userToRebind); } @@ -1488,8 +1471,7 @@ abstract public class ManagedServices { // Filter approvedComponentsByUser to collect all of the components that are allowed // for the currently active user(s). - populateComponentsToBind(componentsToBind, userIds, approvedComponentsByUser, - isVisibleBackgroundUser); + populateComponentsToBind(componentsToBind, userIds, approvedComponentsByUser); // For every current non-system connection, disconnect services that are no longer // approved, or ALL services if we are force rebinding diff --git a/services/core/java/com/android/server/notification/NotificationManagerInternal.java b/services/core/java/com/android/server/notification/NotificationManagerInternal.java index d5d4070ee4c3..52ddb800fa40 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerInternal.java +++ b/services/core/java/com/android/server/notification/NotificationManagerInternal.java @@ -22,8 +22,11 @@ import android.app.NotificationChannelGroup; import android.app.backup.BackupRestoreEventLogger; import android.service.notification.DeviceEffectsApplier; +import com.android.internal.annotations.Keep; + import java.util.Set; +@Keep public interface NotificationManagerInternal { NotificationChannel getNotificationChannel(String pkg, int uid, String channelId); NotificationChannelGroup getNotificationChannelGroup(String pkg, int uid, String channelId); diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 4d0c7ec64317..207764b4e555 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -211,6 +211,7 @@ import android.app.RemoteServiceException.BadForegroundServiceNotificationExcept import android.app.RemoteServiceException.BadUserInitiatedJobNotificationException; import android.app.StatsManager; import android.app.UriGrantsManager; +import android.app.ZenBypassingApp; import android.app.admin.DevicePolicyManagerInternal; import android.app.backup.BackupManager; import android.app.backup.BackupRestoreEventLogger; @@ -238,7 +239,6 @@ import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.LauncherApps; import android.content.pm.ModuleInfo; -import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageManagerInternal; @@ -3089,7 +3089,7 @@ public class NotificationManagerService extends SystemService { migrateDefaultNAS(); maybeShowInitialReviewPermissionsNotification(); - if (android.app.Flags.modesApi()) { + if (android.app.Flags.modesApi() && !mZenModeHelper.hasDeviceEffectsApplier()) { // Cannot be done earlier, as some services aren't ready until this point. mZenModeHelper.setDeviceEffectsApplier( new DefaultDeviceEffectsApplier(getContext())); @@ -4043,7 +4043,7 @@ public class NotificationManagerService extends SystemService { "canNotifyAsPackage for uid " + uid); } - return areNotificationsEnabledForPackageInt(pkg, uid); + return areNotificationsEnabledForPackageInt(uid); } /** @@ -4864,30 +4864,20 @@ public class NotificationManagerService extends SystemService { } @Override - public List<String> getPackagesBypassingDnd(int userId, - boolean includeConversationChannels) { + public ParceledListSlice<ZenBypassingApp> getPackagesBypassingDnd(int userId) + throws RemoteException { checkCallerIsSystem(); - final ArraySet<String> packageNames = new ArraySet<>(); - - List<PackageInfo> pkgs = mPackageManagerClient.getInstalledPackagesAsUser(0, userId); - for (PackageInfo pi : pkgs) { - String pkg = pi.packageName; - // If any NotificationChannel for this package is bypassing, the - // package is considered bypassing. - for (NotificationChannel channel : getNotificationChannelsBypassingDnd(pkg, - pi.applicationInfo.uid).getList()) { - // Skips non-demoted conversation channels. - if (!includeConversationChannels - && !TextUtils.isEmpty(channel.getConversationId()) - && !channel.isDemoted()) { - continue; - } - packageNames.add(pkg); + UserHandle user = UserHandle.of(userId); + ArrayList<ZenBypassingApp> bypassing = + mPreferencesHelper.getPackagesBypassingDnd(userId); + for (int i = bypassing.size() - 1; i >= 0; i--) { + String pkg = bypassing.get(i).getPkg(); + if (!areNotificationsEnabledForPackage(pkg, getUidForPackageAndUser(pkg, user))) { + bypassing.remove(i); } } - - return new ArrayList<String>(packageNames); + return new ParceledListSlice<>(bypassing); } @Override @@ -7763,7 +7753,7 @@ public class NotificationManagerService extends SystemService { @Override public boolean areNotificationsEnabledForPackage(String pkg, int uid) { - return areNotificationsEnabledForPackageInt(pkg, uid); + return areNotificationsEnabledForPackageInt(uid); } @Override @@ -8742,7 +8732,7 @@ public class NotificationManagerService extends SystemService { } // blocked apps - boolean isBlocked = !areNotificationsEnabledForPackageInt(pkg, uid); + boolean isBlocked = !areNotificationsEnabledForPackageInt(uid); synchronized (mNotificationLock) { isBlocked |= isRecordBlockedLocked(r); } @@ -8792,7 +8782,7 @@ public class NotificationManagerService extends SystemService { } } - private boolean areNotificationsEnabledForPackageInt(String pkg, int uid) { + private boolean areNotificationsEnabledForPackageInt(int uid) { return mPermissionHelper.hasPermission(uid); } @@ -9328,7 +9318,7 @@ public class NotificationManagerService extends SystemService { * notifying all listeners to a background thread; false otherwise. */ private boolean postNotification() { - boolean appBanned = !areNotificationsEnabledForPackageInt(pkg, uid); + boolean appBanned = !areNotificationsEnabledForPackageInt(uid); boolean isCallNotification = isCallNotification(pkg, uid); boolean posted = false; synchronized (NotificationManagerService.this.mNotificationLock) { diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java index 9f0b4b0b6299..e6f784c71ef3 100644 --- a/services/core/java/com/android/server/notification/PreferencesHelper.java +++ b/services/core/java/com/android/server/notification/PreferencesHelper.java @@ -57,6 +57,7 @@ import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationChannelGroup; import android.app.NotificationManager; +import android.app.ZenBypassingApp; import android.content.AttributionSource; import android.content.Context; import android.content.pm.ApplicationInfo; @@ -1950,6 +1951,35 @@ public class PreferencesHelper implements RankingConfig { } /** + * Gets all apps that can bypass DND, and a boolean indicating whether all (true) or some + * (false) of its notification channels can currently bypass. + */ + public @NonNull ArrayList<ZenBypassingApp> getPackagesBypassingDnd(@UserIdInt int userId) { + ArrayList<ZenBypassingApp> bypassing = new ArrayList<>(); + synchronized (mLock) { + for (PackagePreferences p : mPackagePreferences.values()) { + if (p.userId != userId) { + continue; + } + int totalChannelCount = p.channels.size(); + int bypassingCount = 0; + if (totalChannelCount == 0) { + continue; + } + for (NotificationChannel channel : p.channels.values()) { + if (channelIsLiveLocked(p, channel) && channel.canBypassDnd()) { + bypassingCount++; + } + } + if (bypassingCount > 0) { + bypassing.add(new ZenBypassingApp(p.pkg, totalChannelCount == bypassingCount)); + } + } + } + return bypassing; + } + + /** * True for pre-O apps that only have the default channel, or pre O apps that have no * channels yet. This method will create the default channel for pre-O apps that don't have it. * Should never be true for O+ targeting apps, but that's enforced on boot/when an app diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index cfeacdf2bb0d..ca4f83fd46f6 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -24,6 +24,8 @@ import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_ENABLED; import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_REMOVED; import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_UNKNOWN; import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_ANY; +import static android.app.backup.NotificationLoggingConstants.DATA_TYPE_ZEN_CONFIG; +import static android.app.backup.NotificationLoggingConstants.ERROR_XML_PARSING; import static android.service.notification.Condition.SOURCE_UNKNOWN; import static android.service.notification.Condition.SOURCE_USER_ACTION; import static android.service.notification.Condition.STATE_FALSE; @@ -44,8 +46,6 @@ import static android.service.notification.ZenModeConfig.isImplicitRuleId; import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE; import static com.android.internal.util.Preconditions.checkArgument; -import static android.app.backup.NotificationLoggingConstants.DATA_TYPE_ZEN_CONFIG; -import static android.app.backup.NotificationLoggingConstants.ERROR_XML_PARSING; import static java.util.Objects.requireNonNull; @@ -303,6 +303,15 @@ public class ZenModeHelper { } /** + * @return whether a {@link DeviceEffectsApplier} has already been set or not + */ + boolean hasDeviceEffectsApplier() { + synchronized (mConfigLock) { + return mDeviceEffectsApplier != null; + } + } + + /** * Set the {@link DeviceEffectsApplier} used to apply the consolidated effects. * * <p>Previously calculated effects (as loaded from the user's {@link ZenModeConfig}) will be diff --git a/services/core/java/com/android/server/os/instrumentation/DynamicInstrumentationManagerService.java b/services/core/java/com/android/server/os/instrumentation/DynamicInstrumentationManagerService.java new file mode 100644 index 000000000000..8ec716077f46 --- /dev/null +++ b/services/core/java/com/android/server/os/instrumentation/DynamicInstrumentationManagerService.java @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.os.instrumentation; + +import static android.Manifest.permission.DYNAMIC_INSTRUMENTATION; +import static android.content.Context.DYNAMIC_INSTRUMENTATION_SERVICE; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.PermissionManuallyEnforced; +import android.content.Context; +import android.os.instrumentation.ExecutableMethodFileOffsets; +import android.os.instrumentation.IDynamicInstrumentationManager; +import android.os.instrumentation.MethodDescriptor; +import android.os.instrumentation.TargetProcess; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.server.SystemService; + +import dalvik.system.VMDebug; + +import java.lang.reflect.Method; + +/** + * System private implementation of the {@link IDynamicInstrumentationManager interface}. + */ +public class DynamicInstrumentationManagerService extends SystemService { + public DynamicInstrumentationManagerService(@NonNull Context context) { + super(context); + } + + @Override + public void onStart() { + publishBinderService(DYNAMIC_INSTRUMENTATION_SERVICE, new BinderService()); + } + + private final class BinderService extends IDynamicInstrumentationManager.Stub { + @Override + @PermissionManuallyEnforced + public @Nullable ExecutableMethodFileOffsets getExecutableMethodFileOffsets( + @NonNull TargetProcess targetProcess, @NonNull MethodDescriptor methodDescriptor) { + if (!com.android.art.flags.Flags.executableMethodFileOffsets()) { + throw new UnsupportedOperationException(); + } + getContext().enforceCallingOrSelfPermission( + DYNAMIC_INSTRUMENTATION, "Caller must have DYNAMIC_INSTRUMENTATION permission"); + + if (targetProcess.processName == null + || !targetProcess.processName.equals("system_server")) { + throw new UnsupportedOperationException( + "system_server is the only supported target process"); + } + + Method method = parseMethodDescriptor( + getClass().getClassLoader(), methodDescriptor); + VMDebug.ExecutableMethodFileOffsets location = + VMDebug.getExecutableMethodFileOffsets(method); + + if (location == null) { + return null; + } + + ExecutableMethodFileOffsets ret = new ExecutableMethodFileOffsets(); + ret.containerPath = location.getContainerPath(); + ret.containerOffset = location.getContainerOffset(); + ret.methodOffset = location.getMethodOffset(); + return ret; + } + } + + @VisibleForTesting + static Method parseMethodDescriptor(ClassLoader classLoader, + @NonNull MethodDescriptor descriptor) { + try { + Class<?> javaClass = classLoader.loadClass(descriptor.fullyQualifiedClassName); + Class<?>[] parameters = new Class[descriptor.fullyQualifiedParameters.length]; + for (int i = 0; i < descriptor.fullyQualifiedParameters.length; i++) { + String typeName = descriptor.fullyQualifiedParameters[i]; + boolean isArrayType = typeName.endsWith("[]"); + if (isArrayType) { + typeName = typeName.substring(0, typeName.length() - 2); + } + switch (typeName) { + case "boolean": + parameters[i] = isArrayType ? boolean.class.arrayType() : boolean.class; + break; + case "byte": + parameters[i] = isArrayType ? byte.class.arrayType() : byte.class; + break; + case "char": + parameters[i] = isArrayType ? char.class.arrayType() : char.class; + break; + case "short": + parameters[i] = isArrayType ? short.class.arrayType() : short.class; + break; + case "int": + parameters[i] = isArrayType ? int.class.arrayType() : int.class; + break; + case "long": + parameters[i] = isArrayType ? long.class.arrayType() : long.class; + break; + case "float": + parameters[i] = isArrayType ? float.class.arrayType() : float.class; + break; + case "double": + parameters[i] = isArrayType ? double.class.arrayType() : double.class; + break; + default: + parameters[i] = isArrayType ? classLoader.loadClass(typeName).arrayType() + : classLoader.loadClass(typeName); + } + } + + return javaClass.getDeclaredMethod(descriptor.methodName, parameters); + } catch (ClassNotFoundException | NoSuchMethodException e) { + throw new IllegalArgumentException( + "The specified method cannot be found. Is this descriptor valid? " + + descriptor, e); + } + } +} diff --git a/services/core/java/com/android/server/pm/BackgroundInstallControlService.java b/services/core/java/com/android/server/pm/BackgroundInstallControlService.java index af2bb17fd0e6..d538bb876b64 100644 --- a/services/core/java/com/android/server/pm/BackgroundInstallControlService.java +++ b/services/core/java/com/android/server/pm/BackgroundInstallControlService.java @@ -265,6 +265,7 @@ public class BackgroundInstallControlService extends SystemService { @Override public void handleMessage(Message msg) { + Slog.d(TAG, "Package event received: " + msg.what); switch (msg.what) { case MSG_USAGE_EVENT_RECEIVED: mService.handleUsageEvent( @@ -326,6 +327,8 @@ public class BackgroundInstallControlService extends SystemService { return; } + Slog.d(TAG, "handlePackageAdd: adding " + packageName + " from " + + userId + " and notifying callbacks"); initBackgroundInstalledPackages(); mBackgroundInstalledPackages.add(userId, packageName); mCallbackHelper.notifyAllCallbacks(userId, packageName, INSTALL_EVENT_TYPE_INSTALL); @@ -364,7 +367,11 @@ public class BackgroundInstallControlService extends SystemService { // ADB sets installerPackageName to null, this creates a loophole to bypass BIC which will be // addressed with b/265203007 private boolean installedByAdb(String initiatingPackageName) { - return PackageManagerServiceUtils.isInstalledByAdb(initiatingPackageName); + if(PackageManagerServiceUtils.isInstalledByAdb(initiatingPackageName)) { + Slog.d(TAG, "handlePackageAdd: is installed by ADB, skipping"); + return true; + } + return false; } private boolean wasForegroundInstallation( @@ -407,6 +414,7 @@ public class BackgroundInstallControlService extends SystemService { if (mBackgroundInstalledPackages.contains(userId, packageName)) { mCallbackHelper.notifyAllCallbacks(userId, packageName, INSTALL_EVENT_TYPE_UNINSTALL); } + Slog.d(TAG, "handlePackageRemove: removing " + packageName + " from " + userId); mBackgroundInstalledPackages.remove(userId, packageName); writeBackgroundInstalledPackagesToDisk(); } diff --git a/services/core/java/com/android/server/pm/InstallDependencyHelper.java b/services/core/java/com/android/server/pm/InstallDependencyHelper.java new file mode 100644 index 000000000000..745665bab5b3 --- /dev/null +++ b/services/core/java/com/android/server/pm/InstallDependencyHelper.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.pm; + +import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SHARED_LIBRARY; + +import android.content.pm.SharedLibraryInfo; +import android.content.pm.parsing.PackageLite; +import android.os.OutcomeReceiver; + +import java.util.List; + +/** + * Helper class to interact with SDK Dependency Installer service. + */ +public class InstallDependencyHelper { + private final SharedLibrariesImpl mSharedLibraries; + + InstallDependencyHelper(SharedLibrariesImpl sharedLibraries) { + mSharedLibraries = sharedLibraries; + } + + void resolveLibraryDependenciesIfNeeded(PackageLite pkg, + OutcomeReceiver<Void, PackageManagerException> callback) { + final List<SharedLibraryInfo> missing; + try { + missing = mSharedLibraries.collectMissingSharedLibraryInfos(pkg); + } catch (PackageManagerException e) { + callback.onError(e); + return; + } + + if (missing.isEmpty()) { + // No need for dependency resolution. Move to installation directly. + callback.onResult(null); + return; + } + + try { + bindToDependencyInstaller(); + } catch (Exception e) { + PackageManagerException pe = new PackageManagerException( + INSTALL_FAILED_MISSING_SHARED_LIBRARY, e.getMessage()); + callback.onError(pe); + } + } + + private void bindToDependencyInstaller() { + throw new IllegalStateException("Failed to bind to Dependency Installer"); + } + + +} diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index ef0997696cd7..eb70748918b6 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -220,6 +220,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements private AppOpsManager mAppOps; private final VerifierController mVerifierController; + private final InstallDependencyHelper mInstallDependencyHelper; private final HandlerThread mInstallThread; private final Handler mInstallHandler; @@ -346,6 +347,8 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements synchronized (mVerificationPolicyPerUser) { mVerificationPolicyPerUser.put(USER_SYSTEM, DEFAULT_VERIFICATION_POLICY); } + mInstallDependencyHelper = new InstallDependencyHelper( + mPm.mInjector.getSharedLibrariesImpl()); LocalServices.getService(SystemServiceManager.class).startService( new Lifecycle(context, this)); @@ -543,7 +546,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements session = PackageInstallerSession.readFromXml(in, mInternalCallback, mContext, mPm, mInstallThread.getLooper(), mStagingManager, mSessionsDir, this, mSilentUpdatePolicy, - mVerifierController); + mVerifierController, mInstallDependencyHelper); } catch (Exception e) { Slog.e(TAG, "Could not read session", e); continue; @@ -1065,7 +1068,8 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements userId, callingUid, installSource, params, createdMillis, 0L, stageDir, stageCid, null, null, false, false, false, false, null, SessionInfo.INVALID_ID, false, false, false, PackageManager.INSTALL_UNKNOWN, "", null, - mVerifierController, verificationPolicy, verificationPolicy); + mVerifierController, verificationPolicy, verificationPolicy, + mInstallDependencyHelper); synchronized (mSessions) { mSessions.put(sessionId, session); diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 2a92de57446d..e156b31c19e1 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -145,6 +145,7 @@ import android.os.FileUtils; import android.os.Handler; import android.os.Looper; import android.os.Message; +import android.os.OutcomeReceiver; import android.os.ParcelFileDescriptor; import android.os.ParcelableException; import android.os.PersistableBundle; @@ -433,6 +434,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { private final StagingManager mStagingManager; @NonNull private final VerifierController mVerifierController; + private final InstallDependencyHelper mInstallDependencyHelper; + final int sessionId; final int userId; final SessionParams params; @@ -1188,7 +1191,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { String sessionErrorMessage, DomainSet preVerifiedDomains, @NonNull VerifierController verifierController, @PackageInstaller.VerificationPolicy int initialVerificationPolicy, - @PackageInstaller.VerificationPolicy int currentVerificationPolicy) { + @PackageInstaller.VerificationPolicy int currentVerificationPolicy, + InstallDependencyHelper installDependencyHelper) { mCallback = callback; mContext = context; mPm = pm; @@ -1200,6 +1204,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { mVerifierController = verifierController; mInitialVerificationPolicy = initialVerificationPolicy; mCurrentVerificationPolicy = new AtomicInteger(currentVerificationPolicy); + mInstallDependencyHelper = installDependencyHelper; this.sessionId = sessionId; this.userId = userId; @@ -1424,6 +1429,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { info.packageSource = params.packageSource; info.applicationEnabledSettingPersistent = params.applicationEnabledSettingPersistent; info.pendingUserActionReason = userActionRequirementToReason(mUserActionRequirement); + info.isAutoInstallingDependenciesEnabled = params.isAutoInstallDependenciesEnabled; } return info; } @@ -2611,6 +2617,13 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { maybeFinishChildSessions(error, msg); } + private void onSessionDependencyResolveFailure(int error, String msg) { + Slog.e(TAG, "Failed to resolve dependency for session " + sessionId); + // Dispatch message to remove session from PackageInstallerService. + dispatchSessionFinished(error, msg, null); + maybeFinishChildSessions(error, msg); + } + private void onSystemDataLoaderUnrecoverable() { final String packageName = getPackageName(); if (TextUtils.isEmpty(packageName)) { @@ -3402,7 +3415,36 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { /* extras= */ null, /* forPreapproval= */ false); return; } - install(); + + if (Flags.sdkDependencyInstaller() + && params.isAutoInstallDependenciesEnabled + && !isMultiPackage()) { + resolveLibraryDependenciesIfNeeded(); + } else { + install(); + } + } + + + private void resolveLibraryDependenciesIfNeeded() { + synchronized (mLock) { + // TODO(b/372862145): Callback should be called on a handler passed as parameter + mInstallDependencyHelper.resolveLibraryDependenciesIfNeeded(mPackageLite, + new OutcomeReceiver<>() { + + @Override + public void onResult(Void result) { + install(); + } + + @Override + public void onError(@NonNull PackageManagerException e) { + final String completeMsg = ExceptionUtils.getCompleteMessage(e); + setSessionFailed(e.error, completeMsg); + onSessionDependencyResolveFailure(e.error, completeMsg); + } + }); + } } /** @@ -6048,7 +6090,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @NonNull StagingManager stagingManager, @NonNull File sessionsDir, @NonNull PackageSessionProvider sessionProvider, @NonNull SilentUpdatePolicy silentUpdatePolicy, - @NonNull VerifierController verifierController) + @NonNull VerifierController verifierController, + @NonNull InstallDependencyHelper installDependencyHelper) throws IOException, XmlPullParserException { final int sessionId = in.getAttributeInt(null, ATTR_SESSION_ID); final int userId = in.getAttributeInt(null, ATTR_USER_ID); @@ -6257,6 +6300,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { stageCid, fileArray, checksumsMap, prepared, committed, destroyed, sealed, childSessionIdsArray, parentSessionId, isReady, isFailed, isApplied, sessionErrorCode, sessionErrorMessage, preVerifiedDomains, verifierController, - initialVerificationPolicy, currentVerificationPolicy); + initialVerificationPolicy, currentVerificationPolicy, installDependencyHelper); } } diff --git a/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java b/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java index a28e3c142220..52e8c52fe6af 100644 --- a/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java +++ b/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java @@ -38,6 +38,7 @@ import android.util.Slog; import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; import java.util.ArrayList; @@ -45,7 +46,8 @@ import java.util.function.BiFunction; /** Helper class to handle PackageMonitorCallback and notify the registered client. This is mainly * used by PackageMonitor to improve the broadcast latency. */ -class PackageMonitorCallbackHelper { +@VisibleForTesting +public class PackageMonitorCallbackHelper { private static final boolean DEBUG = false; private static final String TAG = "PackageMonitorCallbackHelper"; diff --git a/services/core/java/com/android/server/pm/SharedLibrariesImpl.java b/services/core/java/com/android/server/pm/SharedLibrariesImpl.java index 929fccce5265..fc54f6864db0 100644 --- a/services/core/java/com/android/server/pm/SharedLibrariesImpl.java +++ b/services/core/java/com/android/server/pm/SharedLibrariesImpl.java @@ -33,6 +33,7 @@ import android.content.pm.SharedLibraryInfo; import android.content.pm.Signature; import android.content.pm.SigningDetails; import android.content.pm.VersionedPackage; +import android.content.pm.parsing.PackageLite; import android.os.Build; import android.os.Process; import android.os.UserHandle; @@ -83,6 +84,7 @@ public final class SharedLibrariesImpl implements SharedLibrariesRead, Watchable private static final boolean DEBUG_SHARED_LIBRARIES = false; private static final String LIBRARY_TYPE_SDK = "sdk"; + private static final String LIBRARY_TYPE_STATIC = "static shared"; /** * Apps targeting Android S and above need to declare dependencies to the public native @@ -926,18 +928,19 @@ public final class SharedLibrariesImpl implements SharedLibrariesRead, Watchable if (!pkg.getUsesLibraries().isEmpty()) { usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesLibraries(), null, null, null, pkg.getPackageName(), "shared", true, pkg.getTargetSdkVersion(), null, - availablePackages, newLibraries); + availablePackages, newLibraries, null); } if (!pkg.getUsesStaticLibraries().isEmpty()) { usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesStaticLibraries(), pkg.getUsesStaticLibrariesVersions(), pkg.getUsesStaticLibrariesCertDigests(), - null, pkg.getPackageName(), "static shared", true, - pkg.getTargetSdkVersion(), usesLibraryInfos, availablePackages, newLibraries); + null, pkg.getPackageName(), LIBRARY_TYPE_STATIC, true, + pkg.getTargetSdkVersion(), usesLibraryInfos, availablePackages, newLibraries, + null); } if (!pkg.getUsesOptionalLibraries().isEmpty()) { usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesOptionalLibraries(), null, null, null, pkg.getPackageName(), "shared", false, pkg.getTargetSdkVersion(), - usesLibraryInfos, availablePackages, newLibraries); + usesLibraryInfos, availablePackages, newLibraries, null); } if (platformCompat.isChangeEnabledInternal(ENFORCE_NATIVE_SHARED_LIBRARY_DEPENDENCIES, pkg.getPackageName(), pkg.getTargetSdkVersion())) { @@ -945,13 +948,13 @@ public final class SharedLibrariesImpl implements SharedLibrariesRead, Watchable usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesNativeLibraries(), null, null, null, pkg.getPackageName(), "native shared", true, pkg.getTargetSdkVersion(), usesLibraryInfos, availablePackages, - newLibraries); + newLibraries, null); } if (!pkg.getUsesOptionalNativeLibraries().isEmpty()) { usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesOptionalNativeLibraries(), null, null, null, pkg.getPackageName(), "native shared", false, pkg.getTargetSdkVersion(), usesLibraryInfos, availablePackages, - newLibraries); + newLibraries, null); } } if (!pkg.getUsesSdkLibraries().isEmpty()) { @@ -961,11 +964,34 @@ public final class SharedLibrariesImpl implements SharedLibrariesRead, Watchable pkg.getUsesSdkLibrariesVersionsMajor(), pkg.getUsesSdkLibrariesCertDigests(), pkg.getUsesSdkLibrariesOptional(), pkg.getPackageName(), LIBRARY_TYPE_SDK, required, pkg.getTargetSdkVersion(), - usesLibraryInfos, availablePackages, newLibraries); + usesLibraryInfos, availablePackages, newLibraries, null); } return usesLibraryInfos; } + List<SharedLibraryInfo> collectMissingSharedLibraryInfos(PackageLite pkgLite) + throws PackageManagerException { + ArrayList<SharedLibraryInfo> missingSharedLibrary = new ArrayList<>(); + synchronized (mPm.mLock) { + collectSharedLibraryInfos(pkgLite.getUsesSdkLibraries(), + pkgLite.getUsesSdkLibrariesVersionsMajor(), + pkgLite.getUsesSdkLibrariesCertDigests(), + /*libsOptional=*/ null, pkgLite.getPackageName(), LIBRARY_TYPE_SDK, + /*required=*/ true, pkgLite.getTargetSdk(), + /*outUsedLibraries=*/ null, mPm.mPackages, /*newLibraries=*/ null, + missingSharedLibrary); + + collectSharedLibraryInfos(pkgLite.getUsesStaticLibraries(), + pkgLite.getUsesStaticLibrariesVersions(), + pkgLite.getUsesStaticLibrariesCertDigests(), + /*libsOptional=*/ null, pkgLite.getPackageName(), LIBRARY_TYPE_STATIC, + /*required=*/ true, pkgLite.getTargetSdk(), + /*outUsedLibraries=*/ null, mPm.mPackages, /*newLibraries=*/ null, + missingSharedLibrary); + } + return missingSharedLibrary; + } + private ArrayList<SharedLibraryInfo> collectSharedLibraryInfos( @NonNull List<String> requestedLibraries, @Nullable long[] requiredVersions, @Nullable String[][] requiredCertDigests, @@ -973,7 +999,8 @@ public final class SharedLibrariesImpl implements SharedLibrariesRead, Watchable @NonNull String packageName, @NonNull String libraryType, boolean required, int targetSdk, @Nullable ArrayList<SharedLibraryInfo> outUsedLibraries, @NonNull final Map<String, AndroidPackage> availablePackages, - @Nullable final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> newLibraries) + @Nullable final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> newLibraries, + @Nullable final List<SharedLibraryInfo> outMissingSharedLibraryInfos) throws PackageManagerException { final int libCount = requestedLibraries.size(); for (int i = 0; i < libCount; i++) { @@ -986,16 +1013,33 @@ public final class SharedLibrariesImpl implements SharedLibrariesRead, Watchable libName, libVersion, mSharedLibraries, newLibraries); } if (libraryInfo == null) { - // Only allow app be installed if the app specifies the sdk-library dependency is - // optional - if (required || (LIBRARY_TYPE_SDK.equals(libraryType) && (libsOptional != null - && !libsOptional[i]))) { - throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY, - "Package " + packageName + " requires unavailable " + libraryType - + " library " + libName + "; failing!"); - } else if (DEBUG_SHARED_LIBRARIES) { - Slog.i(TAG, "Package " + packageName + " desires unavailable " + libraryType - + " library " + libName + "; ignoring!"); + if (required) { + boolean isSdkOrStatic = libraryType.equals(LIBRARY_TYPE_SDK) + || libraryType.equals(LIBRARY_TYPE_STATIC); + if (isSdkOrStatic && outMissingSharedLibraryInfos != null) { + // TODO(b/372862145): Pass the CertDigest too + // If Dependency Installation is supported, try that instead of failing. + SharedLibraryInfo missingLibrary = new SharedLibraryInfo( + libName, libVersion, SharedLibraryInfo.TYPE_SDK_PACKAGE + ); + outMissingSharedLibraryInfos.add(missingLibrary); + } else { + throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY, + "Package " + packageName + " requires unavailable " + libraryType + + " library " + libName + "; failing!"); + } + } else { + // Only allow app be installed if the app specifies the sdk-library + // dependency is optional + boolean isOptional = libsOptional != null && libsOptional[i]; + if (LIBRARY_TYPE_SDK.equals(libraryType) && !isOptional) { + throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY, + "Package " + packageName + " requires unavailable " + libraryType + + " library " + libName + "; failing!"); + } else if (DEBUG_SHARED_LIBRARIES) { + Slog.i(TAG, "Package " + packageName + " desires unavailable " + libraryType + + " library " + libName + "; ignoring!"); + } } } else { if (requiredVersions != null && requiredCertDigests != null) { diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java index acf62dcdd1ce..09feb18d07bf 100644 --- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java +++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java @@ -36,6 +36,7 @@ import android.content.pm.PackageManagerInternal; import android.content.pm.PermissionInfo; import android.content.pm.ProviderInfo; import android.content.pm.ResolveInfo; +import android.health.connect.HealthPermissions; import android.media.RingtoneManager; import android.media.midi.MidiManager; import android.net.Uri; @@ -48,6 +49,7 @@ import android.os.Process; import android.os.UserHandle; import android.os.storage.StorageManager; import android.permission.PermissionManager; +import android.permission.flags.Flags; import android.print.PrintManager; import android.provider.CalendarContract; import android.provider.ContactsContract; @@ -64,6 +66,7 @@ import android.util.SparseArray; import android.util.Xml; import com.android.internal.R; +import com.android.internal.pm.pkg.parsing.ParsingPackageUtils; import com.android.internal.util.ArrayUtils; import com.android.internal.util.XmlUtils; import com.android.modules.utils.TypedXmlPullParser; @@ -213,8 +216,13 @@ final class DefaultPermissionGrantPolicy { private static final Set<String> SENSORS_PERMISSIONS = new ArraySet<>(); static { - SENSORS_PERMISSIONS.add(Manifest.permission.BODY_SENSORS); - SENSORS_PERMISSIONS.add(Manifest.permission.BODY_SENSORS_BACKGROUND); + if (Flags.replaceBodySensorPermissionEnabled()) { + SENSORS_PERMISSIONS.add(HealthPermissions.READ_HEART_RATE); + SENSORS_PERMISSIONS.add(HealthPermissions.READ_HEALTH_DATA_IN_BACKGROUND); + } else { + SENSORS_PERMISSIONS.add(Manifest.permission.BODY_SENSORS); + SENSORS_PERMISSIONS.add(Manifest.permission.BODY_SENSORS_BACKGROUND); + } } private static final Set<String> STORAGE_PERMISSIONS = new ArraySet<>(); @@ -1632,6 +1640,14 @@ final class DefaultPermissionGrantPolicy { continue; } + // If the trunkstable feature flag is disabled for this + // exception, skip the tag. + if (ParsingPackageUtils.getAconfigFlags().skipCurrentElement( + /* pkg= */ null, parser, /* allowNoNamespace= */ true)) { + XmlUtils.skipCurrentTag(parser); + continue; + } + final boolean fixed = parser.getAttributeBoolean(null, ATTR_FIXED, false); final boolean whitelisted = diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 19406b46f5c0..dda5bcf24d07 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -83,12 +83,11 @@ import static android.view.WindowManagerGlobal.ADD_OKAY; import static android.view.WindowManagerGlobal.ADD_PERMISSION_DENIED; import static android.view.contentprotection.flags.Flags.createAccessibilityOverlayAppOpEnabled; -import static com.android.hardware.input.Flags.emojiAndScreenshotKeycodesAvailable; +import static com.android.hardware.input.Flags.enableNew25q2Keycodes; import static com.android.hardware.input.Flags.enableTalkbackAndMagnifierKeyGestures; import static com.android.hardware.input.Flags.keyboardA11yShortcutControl; import static com.android.hardware.input.Flags.modifierShortcutDump; import static com.android.hardware.input.Flags.useKeyGestureEventHandler; -import static com.android.hardware.input.Flags.useKeyGestureEventHandlerMultiPressGestures; import static com.android.server.flags.Flags.modifierShortcutManagerMultiuser; import static com.android.server.flags.Flags.newBugreportKeyboardShortcut; import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.SCREENSHOT_KEYCHORD_DELAY; @@ -2496,7 +2495,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { private void initKeyCombinationRules() { mKeyCombinationManager = new KeyCombinationManager(mHandler); - if (useKeyGestureEventHandler() && useKeyGestureEventHandlerMultiPressGestures()) { + if (InputSettings.doesKeyGestureEventHandlerSupportMultiKeyGestures()) { return; } final boolean screenshotChordEnabled = mContext.getResources().getBoolean( @@ -3442,7 +3441,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { + keyguardOn() + " canceled=" + event.isCanceled()); } - if (!useKeyGestureEventHandler()) { + if (!InputSettings.doesKeyGestureEventHandlerSupportMultiKeyGestures()) { if (mKeyCombinationManager.isKeyConsumed(event)) { return keyConsumed; } @@ -3993,10 +3992,14 @@ public class PhoneWindowManager implements WindowManagerPolicy { return true; } case KeyEvent.KEYCODE_SCREENSHOT: - if (emojiAndScreenshotKeycodesAvailable() && down && repeatCount == 0) { + if (firstDown) { interceptScreenshotChord(SCREENSHOT_KEY_OTHER, 0 /*pressDelay*/); } return true; + case KeyEvent.KEYCODE_DO_NOT_DISTURB: + case KeyEvent.KEYCODE_LOCK: + case KeyEvent.KEYCODE_FULLSCREEN: + return true; } if (isValidGlobalKey(keyCode) && mGlobalKeyManager.handleGlobalKey(mContext, keyCode, event)) { @@ -5666,9 +5669,23 @@ public class PhoneWindowManager implements WindowManagerPolicy { case KeyEvent.KEYCODE_MACRO_4: result &= ~ACTION_PASS_TO_USER; break; - case KeyEvent.KEYCODE_EMOJI_PICKER: - if (!emojiAndScreenshotKeycodesAvailable()) { - // Don't allow EMOJI_PICKER key to be dispatched until flag is released. + case KeyEvent.KEYCODE_DICTATE: + case KeyEvent.KEYCODE_NEW: + case KeyEvent.KEYCODE_CLOSE: + case KeyEvent.KEYCODE_PRINT: + case KeyEvent.KEYCODE_F13: + case KeyEvent.KEYCODE_F14: + case KeyEvent.KEYCODE_F15: + case KeyEvent.KEYCODE_F16: + case KeyEvent.KEYCODE_F17: + case KeyEvent.KEYCODE_F18: + case KeyEvent.KEYCODE_F19: + case KeyEvent.KEYCODE_F20: + case KeyEvent.KEYCODE_F21: + case KeyEvent.KEYCODE_F22: + case KeyEvent.KEYCODE_F23: + case KeyEvent.KEYCODE_F24: + if (!enableNew25q2Keycodes()) { result &= ~ACTION_PASS_TO_USER; } break; @@ -5702,7 +5719,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { } private void handleKeyGesture(KeyEvent event, boolean interactive, boolean defaultDisplayOn) { - if (mKeyCombinationManager.interceptKey(event, interactive)) { + if (!InputSettings.doesKeyGestureEventHandlerSupportMultiKeyGestures() + && mKeyCombinationManager.interceptKey(event, interactive)) { // handled by combo keys manager. mSingleKeyGestureDetector.reset(); return; @@ -5841,8 +5859,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { public int interceptMotionBeforeQueueingNonInteractive(int displayId, int source, int action, long whenNanos, int policyFlags) { if ((policyFlags & FLAG_WAKE) != 0) { - if (mWindowWakeUpPolicy.wakeUpFromMotion( - whenNanos / 1000000, source, action == MotionEvent.ACTION_DOWN)) { + if (mWindowWakeUpPolicy.wakeUpFromMotion(displayId, whenNanos / 1000000, source, + action == MotionEvent.ACTION_DOWN)) { // Woke up. Pass motion events to user. return ACTION_PASS_TO_USER; } @@ -5856,8 +5874,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { // there will be no dream to intercept the touch and wake into ambient. The device should // wake up in this case. if (isTheaterModeEnabled() && (policyFlags & FLAG_WAKE) != 0) { - if (mWindowWakeUpPolicy.wakeUpFromMotion( - whenNanos / 1000000, source, action == MotionEvent.ACTION_DOWN)) { + if (mWindowWakeUpPolicy.wakeUpFromMotion(displayId, whenNanos / 1000000, source, + action == MotionEvent.ACTION_DOWN)) { // Woke up. Pass motion events to user. return ACTION_PASS_TO_USER; } @@ -6205,7 +6223,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } private void wakeUpFromWakeKey(long eventTime, int keyCode, boolean isDown) { - if (mWindowWakeUpPolicy.wakeUpFromKey(eventTime, keyCode, isDown)) { + if (mWindowWakeUpPolicy.wakeUpFromKey(DEFAULT_DISPLAY, eventTime, keyCode, isDown)) { final boolean keyCanLaunchHome = keyCode == KEYCODE_HOME || keyCode == KEYCODE_POWER; // Start HOME with "reason" extra if sleeping for more than mWakeUpToLastStateTimeout if (shouldWakeUpWithHomeIntent() && keyCanLaunchHome) { diff --git a/services/core/java/com/android/server/policy/WindowWakeUpPolicy.java b/services/core/java/com/android/server/policy/WindowWakeUpPolicy.java index af1ad13f1d15..04dbd1fea5d6 100644 --- a/services/core/java/com/android/server/policy/WindowWakeUpPolicy.java +++ b/services/core/java/com/android/server/policy/WindowWakeUpPolicy.java @@ -25,6 +25,7 @@ import static android.os.PowerManager.WAKE_REASON_WAKE_MOTION; import static android.view.KeyEvent.KEYCODE_POWER; import static com.android.server.policy.Flags.supportInputWakeupDelegate; +import static com.android.server.power.feature.flags.Flags.perDisplayWakeByTouch; import android.annotation.Nullable; import android.content.Context; @@ -107,13 +108,14 @@ class WindowWakeUpPolicy { /** * Wakes up from a key event. * + * @param displayId the id of the display to wake. * @param eventTime the timestamp of the event in {@link SystemClock#uptimeMillis()}. * @param keyCode the {@link android.view.KeyEvent} key code of the key event. * @param isDown {@code true} if the event's action is {@link KeyEvent#ACTION_DOWN}. * @return {@code true} if the policy allows the requested wake up and the request has been * executed; {@code false} otherwise. */ - boolean wakeUpFromKey(long eventTime, int keyCode, boolean isDown) { + boolean wakeUpFromKey(int displayId, long eventTime, int keyCode, boolean isDown) { final boolean wakeAllowedDuringTheaterMode = keyCode == KEYCODE_POWER ? mAllowTheaterModeWakeFromPowerKey @@ -126,22 +128,31 @@ class WindowWakeUpPolicy { && mInputWakeUpDelegate.wakeUpFromKey(eventTime, keyCode, isDown)) { return true; } - wakeUp( - eventTime, - keyCode == KEYCODE_POWER ? WAKE_REASON_POWER_BUTTON : WAKE_REASON_WAKE_KEY, - keyCode == KEYCODE_POWER ? "POWER" : "KEY"); + if (perDisplayWakeByTouch()) { + wakeUp( + displayId, + eventTime, + keyCode == KEYCODE_POWER ? WAKE_REASON_POWER_BUTTON : WAKE_REASON_WAKE_KEY, + keyCode == KEYCODE_POWER ? "POWER" : "KEY"); + } else { + wakeUp( + eventTime, + keyCode == KEYCODE_POWER ? WAKE_REASON_POWER_BUTTON : WAKE_REASON_WAKE_KEY, + keyCode == KEYCODE_POWER ? "POWER" : "KEY"); + } return true; } /** * Wakes up from a motion event. * + * @param displayId the id of the display to wake. * @param eventTime the timestamp of the event in {@link SystemClock#uptimeMillis()}. * @param isDown {@code true} if the event's action is {@link MotionEvent#ACTION_DOWN}. * @return {@code true} if the policy allows the requested wake up and the request has been * executed; {@code false} otherwise. */ - boolean wakeUpFromMotion(long eventTime, int source, boolean isDown) { + boolean wakeUpFromMotion(int displayId, long eventTime, int source, boolean isDown) { if (!canWakeUp(mAllowTheaterModeWakeFromMotion)) { if (DEBUG) Slog.d(TAG, "Unable to wake up from motion."); return false; @@ -150,7 +161,11 @@ class WindowWakeUpPolicy { && mInputWakeUpDelegate.wakeUpFromMotion(eventTime, source, isDown)) { return true; } - wakeUp(eventTime, WAKE_REASON_WAKE_MOTION, "MOTION"); + if (perDisplayWakeByTouch()) { + wakeUp(displayId, eventTime, WAKE_REASON_WAKE_MOTION, "MOTION"); + } else { + wakeUp(eventTime, WAKE_REASON_WAKE_MOTION, "MOTION"); + } return true; } @@ -237,4 +252,12 @@ class WindowWakeUpPolicy { private void wakeUp(long wakeTime, @WakeReason int reason, String details) { mPowerManager.wakeUp(wakeTime, reason, "android.policy:" + details); } + + /** Wakes up given display. */ + private void wakeUp(int displayId, long wakeTime, @WakeReason int reason, String details) { + // If we're given an invalid display id to wake, fall back to waking default display + final int displayIdToWake = + displayId == Display.INVALID_DISPLAY ? Display.DEFAULT_DISPLAY : displayId; + mPowerManager.wakeUp(wakeTime, reason, "android.policy:" + details, displayIdToWake); + } } diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java index 677a2dea665b..028ac57fc5a3 100644 --- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java +++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java @@ -303,6 +303,8 @@ public class BatteryStatsImpl extends BatteryStats { private final GnssPowerStatsCollector mGnssPowerStatsCollector; private final CustomEnergyConsumerPowerStatsCollector mCustomEnergyConsumerPowerStatsCollector; private final SparseBooleanArray mPowerStatsCollectorEnabled = new SparseBooleanArray(); + private boolean mMoveWscLoggingToNotifierEnabled = false; + private ScreenPowerStatsCollector.ScreenUsageTimeRetriever mScreenUsageTimeRetriever = new ScreenPowerStatsCollector.ScreenUsageTimeRetriever() { @@ -5155,7 +5157,7 @@ public class BatteryStatsImpl extends BatteryStats { Uid uidStats = getUidStatsLocked(mappedUid, elapsedRealtimeMs, uptimeMs); uidStats.noteStartWakeLocked(pid, name, type, elapsedRealtimeMs); - if (!mPowerManagerFlags.isMoveWscLoggingToNotifierEnabled()) { + if (!mMoveWscLoggingToNotifierEnabled) { mFrameworkStatsLogger.wakelockStateChanged(mapIsolatedUid(uid), wc, name, uidStats.mProcessState, true /* acquired */, getPowerManagerWakeLockLevel(type)); @@ -5206,7 +5208,7 @@ public class BatteryStatsImpl extends BatteryStats { Uid uidStats = getUidStatsLocked(mappedUid, elapsedRealtimeMs, uptimeMs); uidStats.noteStopWakeLocked(pid, name, type, elapsedRealtimeMs); - if (!mPowerManagerFlags.isMoveWscLoggingToNotifierEnabled()) { + if (!mMoveWscLoggingToNotifierEnabled) { mFrameworkStatsLogger.wakelockStateChanged(mapIsolatedUid(uid), wc, name, uidStats.mProcessState, false/* acquired */, getPowerManagerWakeLockLevel(type)); @@ -15975,6 +15977,15 @@ public class BatteryStatsImpl extends BatteryStats { } } + /** + * Controls where the logging of the WakelockStateChanged atom occurs: + * true = Notifier, false = BatteryStatsImpl. + */ + public void setMoveWscLoggingToNotifierEnabled(boolean enabled) { + synchronized (this) { + mMoveWscLoggingToNotifierEnabled = enabled; + } + } @GuardedBy("this") public void systemServicesReady(Context context) { mConstants.startObserving(context.getContentResolver()); diff --git a/services/core/java/com/android/server/adaptiveauth/AdaptiveAuthService.java b/services/core/java/com/android/server/security/adaptiveauthentication/AdaptiveAuthenticationService.java index 9398c7a854d4..b129fdc1b6e3 100644 --- a/services/core/java/com/android/server/adaptiveauth/AdaptiveAuthService.java +++ b/services/core/java/com/android/server/security/adaptiveauthentication/AdaptiveAuthenticationService.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.adaptiveauth; +package com.android.server.security.adaptiveauthentication; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST; @@ -55,8 +55,8 @@ import java.util.Objects; /** * @hide */ -public class AdaptiveAuthService extends SystemService { - private static final String TAG = "AdaptiveAuthService"; +public class AdaptiveAuthenticationService extends SystemService { + private static final String TAG = "AdaptiveAuthenticationService"; private static final boolean DEBUG = Build.IS_DEBUGGABLE && Log.isLoggable(TAG, Log.DEBUG); @VisibleForTesting @@ -78,12 +78,12 @@ public class AdaptiveAuthService extends SystemService { final SparseIntArray mFailedAttemptsForUser = new SparseIntArray(); private final SparseLongArray mLastLockedTimestamp = new SparseLongArray(); - public AdaptiveAuthService(Context context) { + public AdaptiveAuthenticationService(Context context) { this(context, new LockPatternUtils(context)); } @VisibleForTesting - public AdaptiveAuthService(Context context, LockPatternUtils lockPatternUtils) { + public AdaptiveAuthenticationService(Context context, LockPatternUtils lockPatternUtils) { super(context); mLockPatternUtils = lockPatternUtils; mLockSettings = Objects.requireNonNull( diff --git a/services/core/java/com/android/server/security/adaptiveauthentication/OWNERS b/services/core/java/com/android/server/security/adaptiveauthentication/OWNERS new file mode 100644 index 000000000000..29affcdb81aa --- /dev/null +++ b/services/core/java/com/android/server/security/adaptiveauthentication/OWNERS @@ -0,0 +1,3 @@ +hainingc@google.com +jbolinger@google.com +graciecheng@google.com diff --git a/services/core/java/com/android/server/stats/pull/psi/OWNERS b/services/core/java/com/android/server/stats/pull/psi/OWNERS new file mode 100644 index 000000000000..f72fd7c18925 --- /dev/null +++ b/services/core/java/com/android/server/stats/pull/psi/OWNERS @@ -0,0 +1,9 @@ +jackrichardson@google.com +dbrotikovskaya@google.com +ivokay@google.com +gagapov@google.com +yigitfiliz@google.com +rswang@google.com +evaleriano@google.com +igorstepanov@google.com +iyou@google.com diff --git a/services/core/java/com/android/server/stats/pull/psi/PsiData.java b/services/core/java/com/android/server/stats/pull/psi/PsiData.java new file mode 100644 index 000000000000..d1cbf7468d19 --- /dev/null +++ b/services/core/java/com/android/server/stats/pull/psi/PsiData.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.stats.pull.psi; + +/** + * Wraps PSI (Pressure Stall Information) corresponding to a system resource. See more details about + * PSI, see https://docs.kernel.org/accounting/psi.html#psi-pressure-stall-information. + */ +public class PsiData { + public enum ResourceType { + CPU, + MEMORY, + IO + } + + static class AppsStallInfo { + + /** Past 10s average % of wasted CPU cycles when apps tasks are stalled on mResourceType.*/ + private final float mAvg10SecPercentage; + + /** Past 60s average % of wasted CPU cycles when apps tasks are stalled on mResourceType.*/ + private final float mAvg60SecPercentage; + + /** Past 300s average % of wasted CPU cycles when apps tasks are stalled on mResourceType.*/ + private final float mAvg300SecPercentage; + + /** Total number of microseconds that apps tasks are stalled on mResourceType.*/ + private final long mTotalUsec; + + AppsStallInfo( + float avg10SecPercentage, float avg60SecPercentage, + float avg300SecPercentage, long totalUsec) { + mAvg10SecPercentage = avg10SecPercentage; + mAvg60SecPercentage = avg60SecPercentage; + mAvg300SecPercentage = avg300SecPercentage; + mTotalUsec = totalUsec; + } + } + + /** The system resource type of this {@code PsiData}. */ + private final ResourceType mResourceType; + + /** Info on some tasks are stalled on mResourceType. */ + private final AppsStallInfo mSomeAppsStallInfo; + + /** + * Info on all non-idle tasks are stalled on mResourceType. For the CPU ResourceType, + * all fields will always be 0 as it's undefined. + */ + private final AppsStallInfo mFullAppsStallInfo; + + PsiData( + ResourceType resourceType, + AppsStallInfo someAppsStallInfo, + AppsStallInfo fullAppsStallInfo) { + mResourceType = resourceType; + mSomeAppsStallInfo = someAppsStallInfo; + mFullAppsStallInfo = fullAppsStallInfo; + } + + public ResourceType getResourceType() { + return mResourceType; + } + + public float getSomeAvg10SecPercentage() { + return mSomeAppsStallInfo.mAvg10SecPercentage; } + + public float getSomeAvg60SecPercentage() { + return mSomeAppsStallInfo.mAvg60SecPercentage; } + + public float getSomeAvg300SecPercentage() { + return mSomeAppsStallInfo.mAvg300SecPercentage; } + + public long getSomeTotalUsec() { + return mSomeAppsStallInfo.mTotalUsec; + } + + public float getFullAvg10SecPercentage() { + return mFullAppsStallInfo.mAvg10SecPercentage; + } + + public float getFullAvg60SecPercentage() { + return mFullAppsStallInfo.mAvg60SecPercentage; + } + + public float getFullAvg300SecPercentage() { + return mFullAppsStallInfo.mAvg300SecPercentage; } + + public long getFullTotalUsec() { + return mFullAppsStallInfo.mTotalUsec; + } +} diff --git a/services/core/java/com/android/server/stats/pull/psi/PsiExtractor.java b/services/core/java/com/android/server/stats/pull/psi/PsiExtractor.java new file mode 100644 index 000000000000..5d0d7e161990 --- /dev/null +++ b/services/core/java/com/android/server/stats/pull/psi/PsiExtractor.java @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.stats.pull.psi; + +import static java.util.stream.Collectors.joining; + +import android.annotation.Nullable; +import android.util.Log; + +import java.io.BufferedReader; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.text.MessageFormat; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class PsiExtractor { + private static final String TAG = "PsiExtractor"; + + // Paths for PSI files are guarded by SELinux policy. PCS needs to be explicitly + // allowlisted to access these files. + private static final String PSI_MEMORY_PATH = "/proc/pressure/memory"; + private static final String PSI_IO_PATH = "/proc/pressure/io"; + private static final String PSI_CPU_PATH = "/proc/pressure/cpu"; + + // The patterns matching a line of PSI output such as + // "some avg10=0.12 avg60=0.34 avg300=0.56 total=123456" or + // "full avg10=0.12 avg60=0.34 avg300=0.56 total=123456" to extract the stalling percentage + // values for "some" and "full" line of PSI output respectively. + private static final String PSI_PATTERN_TEMPLATE = + ".*{0} avg10=(\\d+.\\d+) avg60=(\\d+.\\d+) avg300=(\\d+.\\d+) total=(\\d+).*"; + private static final String SOME = "some"; + private static final String FULL = "full"; + private final PsiReader mPsiReader; + + public PsiExtractor() { + mPsiReader = new PsiReader(); + } + public PsiExtractor(PsiReader psiReader) { + mPsiReader = psiReader; + } + + /** + * Parses /pressure/proc/{resourceType} kernel file to extract the Pressure Stall Information + * (PSI), more information: can be found at https://docs.kernel.org/accounting/psi.html. + * + * @param resourceType (Memory/CPU/IO) to get the PSI for. + */ + @Nullable + public PsiData getPsiData(PsiData.ResourceType resourceType) { + String psiFileData; + if (resourceType == PsiData.ResourceType.MEMORY) { + psiFileData = mPsiReader.read(PSI_MEMORY_PATH); + } else if (resourceType == PsiData.ResourceType.IO) { + psiFileData = mPsiReader.read(PSI_IO_PATH); + } else if (resourceType == PsiData.ResourceType.CPU) { + psiFileData = mPsiReader.read(PSI_CPU_PATH); + } else { + Log.w(TAG, "PsiExtractor failure: cannot read kernel source file, returning null"); + return null; + } + return parsePsiData(psiFileData, resourceType); + } + + @Nullable + private static PsiData.AppsStallInfo parsePsiString( + String psiFileData, String appType, PsiData.ResourceType resourceType) { + // There is an extra case of file content: the CPU full is undefined and isn't reported for + // earlier versions. It should be always propagated as 0, but for the current logic purposes + // we will report atom only if at least one value (some/full) is presented. Thus, hardcoding + // the "full" line as 0 only when the "some" line is presented. + if (appType == FULL && resourceType == PsiData.ResourceType.CPU) { + if (psiFileData.contains(SOME) && !psiFileData.contains(FULL)) { + return new PsiData.AppsStallInfo((float) 0.0, (float) 0.0, (float) 0.0, 0); + } + } + + Pattern psiStringPattern = Pattern.compile( + MessageFormat.format(PSI_PATTERN_TEMPLATE, appType)); + Matcher psiLineMatcher = psiStringPattern.matcher(psiFileData); + + // Parsing the line starts with "some" in the expected output. + // The line for "some" should always be present in PSI output. The output must be somehow + // malformed if the line cannot be matched. + if (!psiLineMatcher.find()) { + Log.w(TAG, + "Returning null: the line \"" + appType + "\" is not in expected pattern."); + return null; + } + try { + return new PsiData.AppsStallInfo( + Float.parseFloat(psiLineMatcher.group(1)), + Float.parseFloat(psiLineMatcher.group(2)), + Float.parseFloat(psiLineMatcher.group(3)), + Long.parseLong(psiLineMatcher.group(4))); + } catch (NumberFormatException e) { + Log.w(TAG, + "Returning null: some value in line \"" + appType + + "\" cannot be parsed as numeric."); + return null; + } + } + + @Nullable + private static PsiData parsePsiData( + String psiFileData, PsiData.ResourceType resourceType) { + PsiData.AppsStallInfo someAppsStallInfo = parsePsiString(psiFileData, SOME, resourceType); + PsiData.AppsStallInfo fullAppsStallInfo = parsePsiString(psiFileData, FULL, resourceType); + + if (someAppsStallInfo == null && fullAppsStallInfo == null) { + Log.w(TAG, "Returning empty PSI: some or full line are failed to parse"); + return null; + } else if (someAppsStallInfo == null) { + Log.d(TAG, "Replacing some info with empty PSI record for the resource type " + + resourceType); + someAppsStallInfo = new PsiData.AppsStallInfo( + (float) -1.0, (float) -1.0, (float) -1.0, -1); + } else if (fullAppsStallInfo == null) { + Log.d(TAG, "Replacing full info with empty PSI record for the resource type " + + resourceType); + fullAppsStallInfo = new PsiData.AppsStallInfo( + (float) -1.0, (float) -1.0, (float) -1.0, -1); + } + return new PsiData(resourceType, someAppsStallInfo, fullAppsStallInfo); + } + + /** Dependency class */ + public static class PsiReader { + /** + * Reads file from provided path and returns its content if the file found, null otherwise. + * + * @param filePath file path to read. + */ + @Nullable + public String read(String filePath) { + try (BufferedReader br = + new BufferedReader(new InputStreamReader( + new FileInputStream(filePath)))) { + return br.lines().collect(joining(System.lineSeparator())); + } catch (IOException e) { + Log.w(TAG, "Cannot read file " + filePath); + return null; + } + } + } +} diff --git a/services/core/java/com/android/server/utils/LazyJniRegistrar.java b/services/core/java/com/android/server/utils/LazyJniRegistrar.java new file mode 100644 index 000000000000..ac4a92e12909 --- /dev/null +++ b/services/core/java/com/android/server/utils/LazyJniRegistrar.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.utils; + +import com.android.tools.r8.keepanno.annotations.KeepItemKind; +import com.android.tools.r8.keepanno.annotations.MethodAccessFlags; +import com.android.tools.r8.keepanno.annotations.UsedByNative; + +/** + * Utility class for lazily registering native methods for a given class. + * + * <p><strong>Note: </strong>Most native methods are registered eagerly via the + * native {@code JNI_OnLoad} hook when system server loads its primary native + * lib. However, some classes within system server may be stripped if unused. + * This class offers a way to selectively register their native methods. Such + * register calls should typically be done from that class's {@code static {}} + * init block. + */ +@UsedByNative( + description = "Referenced from JNI in jni/com_android_server_utils_LazyJniRegistrar.cpp", + kind = KeepItemKind.CLASS_AND_MEMBERS, + methodAccess = {MethodAccessFlags.NATIVE}) +public final class LazyJniRegistrar { + + // Note: {@link SystemServer#run} loads the native "android_servers" lib, so no need to do so + // explicitly here. Classes that use this registration must not be initialized before this. + + /** Registers native methods for ConsumerIrService. */ + public static native void registerConsumerIrService(); + + /** Registers native methods for VrManagerService. */ + public static native void registerVrManagerService(); +} diff --git a/services/core/java/com/android/server/utils/OWNERS b/services/core/java/com/android/server/utils/OWNERS index fbc0b56c2eb7..9f1cc8196736 100644 --- a/services/core/java/com/android/server/utils/OWNERS +++ b/services/core/java/com/android/server/utils/OWNERS @@ -10,6 +10,7 @@ per-file Watcher.java = file:/services/core/java/com/android/server/pm/OWNERS per-file Watcher.java = shombert@google.com per-file EventLogger.java = file:/platform/frameworks/av:/media/janitors/media_solutions_OWNERS per-file EventLogger.java = jmtrivi@google.com +per-file LazyJniRegistrar.java = file:/PERFORMANCE_OWNERS # Bug component : 158088 = per-file AnrTimer*.java per-file AnrTimer*.java = file:/PERFORMANCE_OWNERS diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java index 1ff01a6c70bf..9cfe3bab6e0b 100644 --- a/services/core/java/com/android/server/vr/VrManagerService.java +++ b/services/core/java/com/android/server/vr/VrManagerService.java @@ -67,6 +67,7 @@ import com.android.server.FgThread; import com.android.server.LocalServices; import com.android.server.SystemConfig; import com.android.server.SystemService; +import com.android.server.utils.LazyJniRegistrar; import com.android.server.utils.ManagedApplicationService; import com.android.server.utils.ManagedApplicationService.BinderChecker; import com.android.server.utils.ManagedApplicationService.LogEvent; @@ -131,6 +132,10 @@ public class VrManagerService extends SystemService private static native void initializeNative(); private static native void setVrModeNative(boolean enabled); + static { + LazyJniRegistrar.registerVrManagerService(); + } + private final Object mLock = new Object(); private final IBinder mOverlayToken = new Binder(); diff --git a/services/core/java/com/android/server/wallpaper/WallpaperCropper.java b/services/core/java/com/android/server/wallpaper/WallpaperCropper.java index d5bea4adaf8c..b3e68a35764b 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperCropper.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperCropper.java @@ -19,6 +19,7 @@ package com.android.server.wallpaper; import static android.app.WallpaperManager.ORIENTATION_UNKNOWN; import static android.app.WallpaperManager.getOrientation; import static android.app.WallpaperManager.getRotatedOrientation; +import static android.app.Flags.accurateWallpaperDownsampling; import static android.view.Display.DEFAULT_DISPLAY; import static com.android.server.wallpaper.WallpaperUtils.RECORD_FILE; @@ -378,7 +379,14 @@ public class WallpaperCropper { for (int i = 0; i < wallpaper.mCropHints.size(); i++) { Rect adjustedRect = new Rect(wallpaper.mCropHints.valueAt(i)); adjustedRect.offset(-wallpaper.cropHint.left, -wallpaper.cropHint.top); - adjustedRect.scale(1f / wallpaper.mSampleSize); + if (accurateWallpaperDownsampling()) { + adjustedRect.left = (int) (0.5f + adjustedRect.left / wallpaper.mSampleSize); + adjustedRect.top = (int) (0.5f + adjustedRect.top / wallpaper.mSampleSize); + adjustedRect.right = (int) Math.floor(adjustedRect.right / wallpaper.mSampleSize); + adjustedRect.bottom = (int) Math.floor(adjustedRect.bottom / wallpaper.mSampleSize); + } else { + adjustedRect.scale(1f / wallpaper.mSampleSize); + } result.put(wallpaper.mCropHints.keyAt(i), adjustedRect); } return result; @@ -603,6 +611,11 @@ public class WallpaperCropper { float sampleSizeForThisOrientation = Math.max(1f, Math.min( crop.width() / displayForThisOrientation.x, crop.height() / displayForThisOrientation.y)); + if (accurateWallpaperDownsampling()) { + sampleSizeForThisOrientation = Math.max(1f, Math.min( + (float) crop.width() / displayForThisOrientation.x, + (float) crop.height() / displayForThisOrientation.y)); + } sampleSize = Math.min(sampleSize, sampleSizeForThisOrientation); } // If the total crop has more width or height than either the max texture size @@ -746,8 +759,8 @@ public class WallpaperCropper { final ImageDecoder.Source srcData = ImageDecoder.createSource(wallpaper.getWallpaperFile()); final int finalScale = scale; - final int rescaledBitmapWidth = (int) (0.5f + bitmapSize.x / sampleSize); - final int rescaledBitmapHeight = (int) (0.5f + bitmapSize.y / sampleSize); + final int rescaledBitmapWidth = (int) Math.ceil(bitmapSize.x / sampleSize); + final int rescaledBitmapHeight = (int) Math.ceil(bitmapSize.y / sampleSize); Bitmap cropped = ImageDecoder.decodeBitmap(srcData, (decoder, info, src) -> { if (!multiCrop()) decoder.setTargetSampleSize(finalScale); if (multiCrop()) { diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java index 10f096c9031b..d019516cd069 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java @@ -2380,8 +2380,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub SparseArray<Rect> relativeSuggestedCrops = mWallpaperCropper.getRelativeCropHints(wallpaper); Point croppedBitmapSize = new Point( - (int) (0.5f + wallpaper.cropHint.width() / wallpaper.mSampleSize), - (int) (0.5f + wallpaper.cropHint.height() / wallpaper.mSampleSize)); + (int) Math.ceil(wallpaper.cropHint.width() / wallpaper.mSampleSize), + (int) Math.ceil(wallpaper.cropHint.height() / wallpaper.mSampleSize)); if (croppedBitmapSize.equals(0, 0)) { // There is an ImageWallpaper, but there are no crop hints and the bitmap size is // unknown (e.g. the default wallpaper). Return a special "null" value that will be @@ -2410,6 +2410,27 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } @Override + public Bundle getCurrentBitmapCrops(int which, int userId) { + userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), + Binder.getCallingUid(), userId, false, true, "getBitmapCrop", null); + synchronized (mLock) { + checkPermission(READ_WALLPAPER_INTERNAL); + WallpaperData wallpaper = (which == FLAG_LOCK) ? mLockWallpaperMap.get(userId) + : mWallpaperMap.get(userId); + if (wallpaper == null || !mImageWallpaper.equals(wallpaper.getComponent())) { + return null; + } + Bundle bundle = new Bundle(); + for (int i = 0; i < wallpaper.mCropHints.size(); i++) { + String key = String.valueOf(wallpaper.mCropHints.keyAt(i)); + Rect rect = wallpaper.mCropHints.valueAt(i); + bundle.putParcelable(key, rect); + } + return bundle; + } + } + + @Override public List<Rect> getFutureBitmapCrops(Point bitmapSize, List<Point> displaySizes, int[] screenOrientations, List<Rect> crops) { SparseArray<Rect> cropMap = getCropMap(screenOrientations, crops); diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 73ae51c6e64a..1c11c6701643 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -254,6 +254,7 @@ import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; import static org.xmlpull.v1.XmlPullParser.END_TAG; import static org.xmlpull.v1.XmlPullParser.START_TAG; +import android.Manifest; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -5549,8 +5550,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (!visible) { if (mTransitionController.inPlayingTransition(this)) { mTransitionChangeFlags |= FLAG_IS_OCCLUDED; - } else if (mTransitionController.inFinishingTransition(this)) { - mTransitionChangeFlags |= FLAGS_IS_OCCLUDED_NO_ANIMATION; + if (mTransitionController.mFinishingTransition != null + && mTransitionController.mFinishingTransition.isTransientLaunch(this)) { + mTransitionChangeFlags |= FLAGS_IS_OCCLUDED_NO_ANIMATION; + } } } else { mTransitionChangeFlags &= ~FLAG_IS_OCCLUDED; @@ -8893,6 +8896,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mAppCompatController.getAppCompatSizeCompatModePolicy(); if (scmPolicy.hasAppCompatDisplayInsetsWithoutInheritance() + && mAppCompatDisplayInsets != null && !mAppCompatDisplayInsets.mIsInFixedOrientationOrAspectRatioLetterbox) { // App prefers to keep its original size. // If the size compat is from previous fixed orientation letterboxing, we may want to @@ -10308,6 +10312,21 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (pictureInPictureArgs != null && pictureInPictureArgs.hasSourceBoundsHint()) { pictureInPictureArgs.getSourceRectHint().offset(windowBounds.left, windowBounds.top); } + + if (android.app.Flags.enableTvImplicitEnterPipRestriction()) { + PackageManager pm = mAtmService.mContext.getPackageManager(); + if (pictureInPictureArgs.isAutoEnterEnabled() + && pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK) + && pm.checkPermission(Manifest.permission.TV_IMPLICIT_ENTER_PIP, packageName) + == PackageManager.PERMISSION_DENIED) { + Log.i(TAG, + "Auto-enter PiP only allowed on TV if android.permission" + + ".TV_IMPLICIT_ENTER_PIP permission is held by the app."); + PictureInPictureParams.Builder builder = new PictureInPictureParams.Builder(); + builder.setAutoEnterEnabled(false); + pictureInPictureArgs.copyOnlySet(builder.build()); + } + } } private void applyLocaleOverrideIfNeeded(Configuration resolvedConfig) { diff --git a/services/core/java/com/android/server/wm/AppCompatAspectRatioPolicy.java b/services/core/java/com/android/server/wm/AppCompatAspectRatioPolicy.java index fa2c71658022..e8eae4f96a04 100644 --- a/services/core/java/com/android/server/wm/AppCompatAspectRatioPolicy.java +++ b/services/core/java/com/android/server/wm/AppCompatAspectRatioPolicy.java @@ -70,15 +70,8 @@ class AppCompatAspectRatioPolicy { mAppCompatAspectRatioState.reset(); } - float getDesiredAspectRatio(@NonNull Configuration newParentConfig, + private float getDesiredAspectRatio(@NonNull Configuration newParentConfig, @NonNull Rect parentBounds) { - // If in camera compat mode, aspect ratio from the camera compat policy has priority over - // default letterbox aspect ratio. - if (AppCompatCameraPolicy.shouldCameraCompatControlAspectRatio( - mActivityRecord)) { - return AppCompatCameraPolicy.getCameraCompatAspectRatio(mActivityRecord); - } - final float letterboxAspectRatioOverride = mAppCompatOverrides.getAppCompatAspectRatioOverrides() .getFixedOrientationLetterboxAspectRatio(newParentConfig); @@ -120,7 +113,16 @@ class AppCompatAspectRatioPolicy { if (mTransparentPolicy.isRunning()) { return mTransparentPolicy.getInheritedMinAspectRatio(); } + final ActivityInfo info = mActivityRecord.info; + + // If in camera compat mode, aspect ratio from the camera compat policy has priority over + // the default aspect ratio. + if (AppCompatCameraPolicy.shouldCameraCompatControlAspectRatio(mActivityRecord)) { + return Math.max(AppCompatCameraPolicy.getCameraCompatMinAspectRatio(mActivityRecord), + info.getMinAspectRatio()); + } + final AppCompatAspectRatioOverrides aspectRatioOverrides = mAppCompatOverrides.getAppCompatAspectRatioOverrides(); if (aspectRatioOverrides.shouldApplyUserMinAspectRatioOverride()) { diff --git a/services/core/java/com/android/server/wm/AppCompatCameraPolicy.java b/services/core/java/com/android/server/wm/AppCompatCameraPolicy.java index 8c5689c1ef57..8be66ccfbd70 100644 --- a/services/core/java/com/android/server/wm/AppCompatCameraPolicy.java +++ b/services/core/java/com/android/server/wm/AppCompatCameraPolicy.java @@ -234,7 +234,7 @@ class AppCompatCameraPolicy { } // TODO(b/369070416): have policies implement the same interface. - static float getCameraCompatAspectRatio(@NonNull ActivityRecord activity) { + static float getCameraCompatMinAspectRatio(@NonNull ActivityRecord activity) { final AppCompatCameraPolicy cameraPolicy = getAppCompatCameraPolicy(activity); if (cameraPolicy == null) { return 1.0f; diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java index ec171c5e5766..bce8c2be271e 100644 --- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java +++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java @@ -46,10 +46,8 @@ import static com.android.server.wm.ActivityTaskSupervisor.getApplicationLabel; import static com.android.server.wm.PendingRemoteAnimationRegistry.TIMEOUT_MS; import static com.android.window.flags.Flags.balAdditionalStartModes; import static com.android.window.flags.Flags.balDontBringExistingBackgroundTaskStackToFg; -import static com.android.window.flags.Flags.balImproveRealCallerVisibilityCheck; import static com.android.window.flags.Flags.balImprovedMetrics; import static com.android.window.flags.Flags.balRequireOptInByPendingIntentCreator; -import static com.android.window.flags.Flags.balRespectAppSwitchStateWhenCheckBoundByForegroundUid; import static com.android.window.flags.Flags.balShowToastsBlocked; import static com.android.window.flags.Flags.balStrictModeRo; @@ -299,7 +297,8 @@ public class BackgroundActivityStartController { private final int mCallingUid; private final int mCallingPid; private final @ActivityTaskManagerService.AppSwitchState int mAppSwitchState; - private final boolean mCallingUidHasAnyVisibleWindow; + private final boolean mCallingUidHasVisibleActivity; + private final boolean mCallingUidHasNonAppVisibleWindow; private final @ActivityManager.ProcessState int mCallingUidProcState; private final boolean mIsCallingUidPersistentSystemProcess; final BackgroundStartPrivileges mBalAllowedByPiSender; @@ -308,7 +307,8 @@ public class BackgroundActivityStartController { private final String mRealCallingPackage; private final int mRealCallingUid; private final int mRealCallingPid; - private final boolean mRealCallingUidHasAnyVisibleWindow; + private final boolean mRealCallingUidHasVisibleActivity; + private final boolean mRealCallingUidHasNonAppVisibleWindow; private final @ActivityManager.ProcessState int mRealCallingUidProcState; private final boolean mIsRealCallingUidPersistentSystemProcess; private final PendingIntentRecord mOriginatingPendingIntent; @@ -348,11 +348,7 @@ public class BackgroundActivityStartController { @BackgroundActivityStartMode int realCallerBackgroundActivityStartMode = checkedOptions.getPendingIntentBackgroundActivityStartMode(); - if (!balImproveRealCallerVisibilityCheck()) { - // without this fix the auto-opt ins below would violate CTS tests - mAutoOptInReason = null; - mAutoOptInCaller = false; - } else if (originatingPendingIntent == null) { + if (originatingPendingIntent == null) { mAutoOptInReason = AUTO_OPT_IN_NOT_PENDING_INTENT; mAutoOptInCaller = true; } else if (mIsCallForResult) { @@ -407,16 +403,21 @@ public class BackgroundActivityStartController { mCallingUidProcState = mService.mActiveUids.getUidState(callingUid); mIsCallingUidPersistentSystemProcess = mCallingUidProcState <= ActivityManager.PROCESS_STATE_PERSISTENT_UI; - mCallingUidHasAnyVisibleWindow = mService.hasActiveVisibleWindow(callingUid); + mCallingUidHasVisibleActivity = + mService.mVisibleActivityProcessTracker.hasVisibleActivity(callingUid); + mCallingUidHasNonAppVisibleWindow = mService.mActiveUids.hasNonAppVisibleWindow( + callingUid); if (realCallingUid == NO_PROCESS_UID) { // no process provided mRealCallingUidProcState = PROCESS_STATE_NONEXISTENT; - mRealCallingUidHasAnyVisibleWindow = false; + mRealCallingUidHasVisibleActivity = false; + mRealCallingUidHasNonAppVisibleWindow = false; mRealCallerApp = null; mIsRealCallingUidPersistentSystemProcess = false; } else if (callingUid == realCallingUid) { mRealCallingUidProcState = mCallingUidProcState; - mRealCallingUidHasAnyVisibleWindow = mCallingUidHasAnyVisibleWindow; + mRealCallingUidHasVisibleActivity = mCallingUidHasVisibleActivity; + mRealCallingUidHasNonAppVisibleWindow = mCallingUidHasNonAppVisibleWindow; // In the PendingIntent case callerApp is not passed in, so resolve it ourselves. mRealCallerApp = callerApp == null ? mService.getProcessController(realCallingPid, realCallingUid) @@ -424,8 +425,10 @@ public class BackgroundActivityStartController { mIsRealCallingUidPersistentSystemProcess = mIsCallingUidPersistentSystemProcess; } else { mRealCallingUidProcState = mService.mActiveUids.getUidState(realCallingUid); - mRealCallingUidHasAnyVisibleWindow = - mService.hasActiveVisibleWindow(realCallingUid); + mRealCallingUidHasVisibleActivity = + mService.mVisibleActivityProcessTracker.hasVisibleActivity(realCallingUid); + mRealCallingUidHasNonAppVisibleWindow = + mService.mActiveUids.hasNonAppVisibleWindow(realCallingUid); mRealCallerApp = mService.getProcessController(realCallingPid, realCallingUid); mIsRealCallingUidPersistentSystemProcess = mRealCallingUidProcState <= ActivityManager.PROCESS_STATE_PERSISTENT_UI; @@ -552,7 +555,9 @@ public class BackgroundActivityStartController { sb.append("; callingUid: ").append(mCallingUid); sb.append("; callingPid: ").append(mCallingPid); sb.append("; appSwitchState: ").append(mAppSwitchState); - sb.append("; callingUidHasAnyVisibleWindow: ").append(mCallingUidHasAnyVisibleWindow); + sb.append("; callingUidHasVisibleActivity: ").append(mCallingUidHasVisibleActivity); + sb.append("; callingUidHasNonAppVisibleWindow: ").append( + mCallingUidHasNonAppVisibleWindow); sb.append("; callingUidProcState: ").append(DebugUtils.valueToString( ActivityManager.class, "PROCESS_STATE_", mCallingUidProcState)); sb.append("; isCallingUidPersistentSystemProcess: ") @@ -581,8 +586,10 @@ public class BackgroundActivityStartController { .append(getTargetSdk(mRealCallingPackage)); sb.append("; realCallingUid: ").append(mRealCallingUid); sb.append("; realCallingPid: ").append(mRealCallingPid); - sb.append("; realCallingUidHasAnyVisibleWindow: ") - .append(mRealCallingUidHasAnyVisibleWindow); + sb.append("; realCallingUidHasVisibleActivity: ") + .append(mRealCallingUidHasVisibleActivity); + sb.append("; realCallingUidHasNonAppVisibleWindow: ") + .append(mRealCallingUidHasNonAppVisibleWindow); sb.append("; realCallingUidProcState: ").append(DebugUtils.valueToString( ActivityManager.class, "PROCESS_STATE_", mRealCallingUidProcState)); sb.append("; isRealCallingUidPersistentSystemProcess: ") @@ -599,12 +606,8 @@ public class BackgroundActivityStartController { mCheckedOptions.getPendingIntentBackgroundActivityStartMode())); } // features - sb.append("; balImproveRealCallerVisibilityCheck: ") - .append(balImproveRealCallerVisibilityCheck()); sb.append("; balRequireOptInByPendingIntentCreator: ") .append(balRequireOptInByPendingIntentCreator()); - sb.append("; balRespectAppSwitchStateWhenCheckBoundByForegroundUid: ") - .append(balRespectAppSwitchStateWhenCheckBoundByForegroundUid()); sb.append("; balDontBringExistingBackgroundTaskStackToFg: ") .append(balDontBringExistingBackgroundTaskStackToFg()); sb.append("]"); @@ -1007,11 +1010,11 @@ public class BackgroundActivityStartController { // is allowed, or apps like live wallpaper with non app visible window will be allowed. final boolean appSwitchAllowedOrFg = state.mAppSwitchState == APP_SWITCH_ALLOW || state.mAppSwitchState == APP_SWITCH_FG_ONLY; - if (appSwitchAllowedOrFg && state.mCallingUidHasAnyVisibleWindow) { + if (appSwitchAllowedOrFg && state.mCallingUidHasVisibleActivity) { return new BalVerdict(BAL_ALLOW_VISIBLE_WINDOW, /*background*/ false, "callingUid has visible window"); } - if (mService.mActiveUids.hasNonAppVisibleWindow(state.mCallingUid)) { + if (state.mCallingUidHasNonAppVisibleWindow) { return new BalVerdict(BAL_ALLOW_NON_APP_VISIBLE_WINDOW, /*background*/ false, "callingUid has non-app visible window"); } @@ -1133,23 +1136,13 @@ public class BackgroundActivityStartController { final boolean appSwitchAllowedOrFg = state.mAppSwitchState == APP_SWITCH_ALLOW || state.mAppSwitchState == APP_SWITCH_FG_ONLY || isHomeApp(state.mRealCallingUid, state.mRealCallingPackage); - if (balImproveRealCallerVisibilityCheck()) { - if (appSwitchAllowedOrFg && state.mRealCallingUidHasAnyVisibleWindow) { - return new BalVerdict(BAL_ALLOW_VISIBLE_WINDOW, - /*background*/ false, "realCallingUid has visible window"); - } - if (mService.mActiveUids.hasNonAppVisibleWindow(state.mRealCallingUid)) { - return new BalVerdict(BAL_ALLOW_NON_APP_VISIBLE_WINDOW, - /*background*/ false, "realCallingUid has non-app visible window"); - } - } else { - // don't abort if the realCallingUid has a visible window - // TODO(b/171459802): We should check appSwitchAllowed also - if (state.mRealCallingUidHasAnyVisibleWindow) { - return new BalVerdict(BAL_ALLOW_VISIBLE_WINDOW, - /*background*/ false, - "realCallingUid has visible (non-toast) window."); - } + if (appSwitchAllowedOrFg && state.mRealCallingUidHasVisibleActivity) { + return new BalVerdict(BAL_ALLOW_VISIBLE_WINDOW, + /*background*/ false, "realCallingUid has visible window"); + } + if (state.mRealCallingUidHasNonAppVisibleWindow) { + return new BalVerdict(BAL_ALLOW_NON_APP_VISIBLE_WINDOW, + /*background*/ false, "realCallingUid has non-app visible window"); } // Don't abort if the realCallerApp or other processes of that uid are considered to be in @@ -1892,10 +1885,12 @@ public class BackgroundActivityStartController { state.mCallingUid, state.mCallingPackage, state.mCallingUidProcState, - state.mCallingUidHasAnyVisibleWindow, + state.mCallingUidHasVisibleActivity + || state.mCallingUidHasNonAppVisibleWindow, state.mRealCallingUid, state.mRealCallingUidProcState, - state.mRealCallingUidHasAnyVisibleWindow, + state.mRealCallingUidHasVisibleActivity + || state.mRealCallingUidHasNonAppVisibleWindow, (state.mOriginatingPendingIntent != null)); } diff --git a/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java b/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java index 264c8beb44bf..ccf1aedb3177 100644 --- a/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java +++ b/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java @@ -50,7 +50,6 @@ import android.util.IntArray; import com.android.internal.annotations.GuardedBy; import com.android.server.wm.BackgroundActivityStartController.BalVerdict; -import com.android.window.flags.Flags; import java.io.PrintWriter; import java.util.ArrayList; @@ -137,10 +136,8 @@ class BackgroundLaunchProcessController { } // Allow if the caller is bound by a UID that's currently foreground. // But still respect the appSwitchState. - if (checkConfiguration.checkVisibility && ( - Flags.balRespectAppSwitchStateWhenCheckBoundByForegroundUid() - ? appSwitchState != APP_SWITCH_DISALLOW && isBoundByForegroundUid() - : isBoundByForegroundUid())) { + if (checkConfiguration.checkVisibility && appSwitchState != APP_SWITCH_DISALLOW + && isBoundByForegroundUid()) { return new BalVerdict(balImprovedMetrics() ? BAL_ALLOW_BOUND_BY_FOREGROUND : BAL_ALLOW_VISIBLE_WINDOW, /*background*/ false, "process bound by foreground uid"); diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java index 5ed9612e4e83..49f717e228d2 100644 --- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java +++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java @@ -111,6 +111,7 @@ final class ImeInsetsSourceProvider extends InsetsSourceProvider { // If the server visibility didn't change (still visible), and mGivenInsetsReady // is set, we won't call into notifyControlChanged. Therefore, we can reset the // statsToken, if available. + ProtoLog.d(WM_DEBUG_IME, "onPostLayout cancel statsToken, ws=%s", ws); ImeTracker.forLogging().onCancelled(mStatsToken, ImeTracker.PHASE_WM_POST_LAYOUT_NOTIFY_CONTROLS_CHANGED); mStatsToken = null; @@ -174,9 +175,13 @@ final class ImeInsetsSourceProvider extends InsetsSourceProvider { if (android.view.inputmethod.Flags.refactorInsetsController()) { if (control != null && control.getLeash() != null) { ImeTracker.Token statsToken = getAndClearStatsToken(); - ImeTracker.forLogging().onProgress(statsToken, - ImeTracker.PHASE_WM_GET_CONTROL_WITH_LEASH); - control.setImeStatsToken(statsToken); + if (statsToken == null) { + ProtoLog.d(WM_DEBUG_IME, "IME getControl without statsToken"); + } else { + ImeTracker.forLogging().onProgress(statsToken, + ImeTracker.PHASE_WM_GET_CONTROL_WITH_LEASH); + control.setImeStatsToken(statsToken); + } } } return control; diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java index 2664d8c9cfe4..6091b8334438 100644 --- a/services/core/java/com/android/server/wm/KeyguardController.java +++ b/services/core/java/com/android/server/wm/KeyguardController.java @@ -771,20 +771,21 @@ class KeyguardController { mTopTurnScreenOnActivity.setCurrentLaunchCanTurnScreenOn(false); } - boolean hasChange = false; - if (!lastKeyguardGoingAway && mKeyguardGoingAway) { + final boolean startedGoingAway = (!lastKeyguardGoingAway && mKeyguardGoingAway); + final boolean occludedChanged = (lastOccluded != mOccluded); + + if (startedGoingAway) { writeEventLog("dismissIfInsecure"); controller.handleDismissInsecureKeyguard(display); controller.scheduleGoingAwayTimeout(mDisplayId); - hasChange = true; - } else if (lastOccluded != mOccluded) { + } + if (occludedChanged && (reduceKeyguardTransitions() || !startedGoingAway)) { controller.handleOccludedChanged(mDisplayId, mTopOccludesActivity); - hasChange = true; } // Collect the participants for shell transition, so that transition won't happen too // early since the transition was set ready. - if (hasChange && top != null && (mOccluded || mKeyguardGoingAway)) { + if (top != null && (startedGoingAway || (occludedChanged && mOccluded))) { display.mTransitionController.collect(top); } } diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java index 3f6e91590cce..9a48d5b8880d 100644 --- a/services/core/java/com/android/server/wm/SurfaceAnimator.java +++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java @@ -18,7 +18,6 @@ package com.android.server.wm; import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_ANIM; import static com.android.server.wm.SurfaceAnimatorProto.ANIMATION_ADAPTER; -import static com.android.server.wm.SurfaceAnimatorProto.ANIMATION_START_DELAYED; import static com.android.server.wm.SurfaceAnimatorProto.LEASH; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; @@ -90,8 +89,6 @@ public class SurfaceAnimator { @Nullable private Runnable mAnimationCancelledCallback; - private boolean mAnimationStartDelayed; - private boolean mAnimationFinished; /** @@ -188,10 +185,6 @@ public class SurfaceAnimator { mAnimatable.onAnimationLeashCreated(t, mLeash); } mAnimatable.onLeashAnimationStarting(t, mLeash); - if (mAnimationStartDelayed) { - ProtoLog.i(WM_DEBUG_ANIM, "Animation start delayed for %s", mAnimatable); - return; - } mAnimation.startAnimation(mLeash, t, type, mInnerAnimationFinishedCallback); if (ProtoLog.isEnabled(WM_DEBUG_ANIM, LogLevel.DEBUG)) { StringWriter sw = new StringWriter(); @@ -215,36 +208,7 @@ public class SurfaceAnimator { null /* animationCancelledCallback */, null /* snapshotAnim */, null /* freezer */); } - /** - * Begins with delaying all animations to start. Any subsequent call to {@link #startAnimation} - * will not start the animation until {@link #endDelayingAnimationStart} is called. When an - * animation start is being delayed, the animator is considered animating already. - */ - void startDelayingAnimationStart() { - - // We only allow delaying animation start we are not currently animating - if (!isAnimating()) { - mAnimationStartDelayed = true; - } - } - - /** - * See {@link #startDelayingAnimationStart}. - */ - void endDelayingAnimationStart() { - final boolean delayed = mAnimationStartDelayed; - mAnimationStartDelayed = false; - if (delayed && mAnimation != null) { - mAnimation.startAnimation(mLeash, mAnimatable.getSyncTransaction(), - mAnimationType, mInnerAnimationFinishedCallback); - mAnimatable.commitPendingTransaction(); - } - } - - /** - * @return Whether we are currently running an animation, or we have a pending animation that - * is waiting to be started with {@link #endDelayingAnimationStart} - */ + /** Returns whether it is currently running an animation. */ boolean isAnimating() { return mAnimation != null; } @@ -290,15 +254,6 @@ public class SurfaceAnimator { } /** - * Reparents the surface. - * - * @see #setLayer - */ - void reparent(Transaction t, SurfaceControl newParent) { - t.reparent(mLeash != null ? mLeash : mAnimatable.getSurfaceControl(), newParent); - } - - /** * @return True if the surface is attached to the leash; false otherwise. */ boolean hasLeash() { @@ -319,7 +274,6 @@ public class SurfaceAnimator { Slog.w(TAG, "Unable to transfer animation, because " + from + " animation is finished"); return; } - endDelayingAnimationStart(); final Transaction t = mAnimatable.getSyncTransaction(); cancelAnimation(t, true /* restarting */, true /* forwardCancel */); mLeash = from.mLeash; @@ -336,10 +290,6 @@ public class SurfaceAnimator { mService.mAnimationTransferMap.put(mAnimation, this); } - boolean isAnimationStartDelayed() { - return mAnimationStartDelayed; - } - /** * Cancels the animation, and resets the leash. * @@ -361,7 +311,7 @@ public class SurfaceAnimator { final SurfaceFreezer.Snapshot snapshot = mSnapshot; reset(t, false); if (animation != null) { - if (!mAnimationStartDelayed && forwardCancel) { + if (forwardCancel) { animation.onAnimationCancelled(leash); if (animationCancelledCallback != null) { animationCancelledCallback.run(); @@ -386,10 +336,6 @@ public class SurfaceAnimator { mService.scheduleAnimationLocked(); } } - - if (!restarting) { - mAnimationStartDelayed = false; - } } private void reset(Transaction t, boolean destroyLeash) { @@ -495,14 +441,12 @@ public class SurfaceAnimator { if (mLeash != null) { mLeash.dumpDebug(proto, LEASH); } - proto.write(ANIMATION_START_DELAYED, mAnimationStartDelayed); proto.end(token); } void dump(PrintWriter pw, String prefix) { pw.print(prefix); pw.print("mLeash="); pw.print(mLeash); - pw.print(" mAnimationType=" + animationTypeToString(mAnimationType)); - pw.println(mAnimationStartDelayed ? " mAnimationStartDelayed=true" : ""); + pw.print(" mAnimationType="); pw.println(animationTypeToString(mAnimationType)); pw.print(prefix); pw.print("Animation: "); pw.println(mAnimation); if (mAnimation != null) { mAnimation.dump(pw, prefix + " "); diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index a6034664af5a..20481f25fa5c 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -465,6 +465,31 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { return false; } + /** + * This ensures that all changes for previously transient-hide containers are flagged such that + * they will report changes and be included in this transition. + */ + void updateChangesForRestoreTransientHideTasks(Transition transientLaunchTransition) { + if (transientLaunchTransition.mTransientHideTasks == null) { + // Skip if the transient-launch transition has no transient-hide tasks + ProtoLog.v(WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS, + "Skipping update changes for restore transient hide tasks"); + return; + } + + // For each change, if it was previously transient-hidden, then we should force a flag to + // ensure that it is included in the next transition + for (int i = 0; i < mChanges.size(); i++) { + final WindowContainer container = mChanges.keyAt(i); + if (transientLaunchTransition.isInTransientHide(container)) { + ProtoLog.v(WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS, + "Force update transient hide task for restore %d: %s", mSyncId, container); + final ChangeInfo info = mChanges.valueAt(i); + info.mRestoringTransientHide = true; + } + } + } + /** Returns {@code true} if the task should keep visible if this is a transient transition. */ boolean isTransientVisible(@NonNull Task task) { if (mTransientLaunches == null) return false; @@ -3478,6 +3503,10 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { // State tracking boolean mExistenceChanged = false; + // This state indicates that we are restoring transient order as a part of an + // end-transition. Because the visibility for transient hide containers has not actually + // changed, we need to ensure that hasChanged() still reports the relevant changes + boolean mRestoringTransientHide = false; // before change state boolean mVisible; int mWindowingMode; @@ -3552,7 +3581,11 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { || !mContainer.getBounds().equals(mAbsoluteBounds) || mRotation != mContainer.getWindowConfiguration().getRotation() || mDisplayId != getDisplayId(mContainer) - || (mFlags & ChangeInfo.FLAG_CHANGE_MOVED_TO_TOP) != 0; + || (mFlags & ChangeInfo.FLAG_CHANGE_MOVED_TO_TOP) != 0 + // If we are restoring transient-hide containers, then we should consider them + // important for the transition as well (their requested visibilities would not + // have changed for the checks below to consider it). + || mRestoringTransientHide; } @TransitionInfo.TransitionMode @@ -3565,6 +3598,11 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { } final boolean nowVisible = wc.isVisibleRequested(); if (nowVisible == mVisible) { + if (mRestoringTransientHide) { + // The requested visibility has not changed for transient-hide containers, but + // we are restoring them so we should considering them moving to front again + return TRANSIT_TO_FRONT; + } return TRANSIT_CHANGE; } if (mExistenceChanged) { diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java index 87bdfa4f5d75..143d1b72fff9 100644 --- a/services/core/java/com/android/server/wm/TransitionController.java +++ b/services/core/java/com/android/server/wm/TransitionController.java @@ -37,6 +37,7 @@ import android.os.RemoteException; import android.os.SystemClock; import android.os.SystemProperties; import android.util.ArrayMap; +import android.util.Pair; import android.util.Slog; import android.util.SparseArray; import android.util.TimeUtils; @@ -524,6 +525,23 @@ class TransitionController { return false; } + /** + * @return A pair of the transition and restore-behind target for the given {@param container}. + * @param container An ancestor of a transient-launch activity + */ + @Nullable + Pair<Transition, Task> getTransientLaunchTransitionAndTarget( + @NonNull WindowContainer container) { + for (int i = mPlayingTransitions.size() - 1; i >= 0; --i) { + final Transition transition = mPlayingTransitions.get(i); + final Task restoreBehindTask = transition.getTransientLaunchRestoreTarget(container); + if (restoreBehindTask != null) { + return new Pair<>(transition, restoreBehindTask); + } + } + return null; + } + /** Returns {@code true} if the display contains a transient-launch transition. */ boolean hasTransientLaunch(@NonNull DisplayContent dc) { if (mCollectingTransition != null && mCollectingTransition.hasTransientLaunch() diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index e0c473de0f33..5f92bb626154 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -3215,8 +3215,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< final boolean isChanging = AppTransition.isChangeTransitOld(transit) && enter && isChangingAppTransition(); - // Delaying animation start isn't compatible with remote animations at all. - if (controller != null && !mSurfaceAnimator.isAnimationStartDelayed()) { + if (controller != null) { // Here we load App XML in order to read com.android.R.styleable#Animation_showBackdrop. boolean showBackdrop = false; // Optionally set backdrop color if App explicitly provides it through @@ -3639,20 +3638,6 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< return getAnimatingContainer(PARENTS, ANIMATION_TYPE_ALL); } - /** - * @see SurfaceAnimator#startDelayingAnimationStart - */ - void startDelayingAnimationStart() { - mSurfaceAnimator.startDelayingAnimationStart(); - } - - /** - * @see SurfaceAnimator#endDelayingAnimationStart - */ - void endDelayingAnimationStart() { - mSurfaceAnimator.endDelayingAnimationStart(); - } - @Override public int getSurfaceWidth() { return mSurfaceControl.getWidth(); diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index dac8f69a4cae..ead12826c263 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -111,6 +111,7 @@ import android.os.RemoteException; import android.util.AndroidRuntimeException; import android.util.ArrayMap; import android.util.ArraySet; +import android.util.Pair; import android.util.Slog; import android.view.RemoteAnimationAdapter; import android.view.SurfaceControl; @@ -1375,16 +1376,56 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub break; } case HIERARCHY_OP_TYPE_RESTORE_TRANSIENT_ORDER: { - if (!chain.isFinishing()) break; + if (!com.android.wm.shell.Flags.enableShellTopTaskTracking()) { + // Only allow restoring transient order when finishing a transition + if (!chain.isFinishing()) break; + } + // Validate the container final WindowContainer container = WindowContainer.fromBinder(hop.getContainer()); - if (container == null) break; + if (container == null) { + ProtoLog.v(WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS, + "Restoring transient order: invalid container"); + break; + } final Task thisTask = container.asActivityRecord() != null ? container.asActivityRecord().getTask() : container.asTask(); - if (thisTask == null) break; - final Task restoreAt = chain.mTransition.getTransientLaunchRestoreTarget(container); - if (restoreAt == null) break; + if (thisTask == null) { + ProtoLog.v(WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS, + "Restoring transient order: invalid task"); + break; + } + + // Find the task to restore behind + final Pair<Transition, Task> transientRestore = + mTransitionController.getTransientLaunchTransitionAndTarget(container); + if (transientRestore == null) { + ProtoLog.v(WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS, + "Restoring transient order: no restore task"); + break; + } + final Transition transientLaunchTransition = transientRestore.first; + final Task restoreAt = transientRestore.second; + ProtoLog.v(WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS, + "Restoring transient order: restoring behind task=%d", restoreAt.mTaskId); + + // Restore the position of the given container behind the target task final TaskDisplayArea taskDisplayArea = thisTask.getTaskDisplayArea(); taskDisplayArea.moveRootTaskBehindRootTask(thisTask.getRootTask(), restoreAt); + + if (com.android.wm.shell.Flags.enableShellTopTaskTracking()) { + // Because we are in a transient launch transition, the requested visibility of + // tasks does not actually change for the transient-hide tasks, but we do want + // the restoration of these transient-hide tasks to top to be a part of this + // finish transition + final Transition collectingTransition = + mTransitionController.getCollectingTransition(); + if (collectingTransition != null) { + collectingTransition.updateChangesForRestoreTransientHideTasks( + transientLaunchTransition); + } + } + + effects |= TRANSACT_EFFECTS_LIFECYCLE; break; } case HIERARCHY_OP_TYPE_ADD_INSETS_FRAME_PROVIDER: { diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp index 4dc3ca5ceea5..eaa3a37d5bf3 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -75,6 +75,7 @@ cc_library_static { "com_android_server_am_LowMemDetector.cpp", "com_android_server_pm_PackageManagerShellCommandDataLoader.cpp", "com_android_server_sensor_SensorService.cpp", + "com_android_server_utils_LazyJniRegistrar.cpp", "com_android_server_wm_TaskFpsCallbackController.cpp", "onload.cpp", ":lib_cachedAppOptimizer_native", diff --git a/services/core/jni/OWNERS b/services/core/jni/OWNERS index b622751fc3e8..8052b092a1e1 100644 --- a/services/core/jni/OWNERS +++ b/services/core/jni/OWNERS @@ -30,6 +30,7 @@ per-file com_android_server_vibrator_* = file:/services/core/java/com/android/se per-file com_android_server_am_CachedAppOptimizer.cpp = file:/PERFORMANCE_OWNERS per-file com_android_server_am_Freezer.cpp = file:/PERFORMANCE_OWNERS per-file com_android_server_companion_virtual_InputController.cpp = file:/services/companion/java/com/android/server/companion/virtual/OWNERS +per-file com_android_server_utils_LazyJniRegistrar.cpp = file:/PERFORMANCE_OWNERS # Memory per-file com_android_server_am_OomConnection.cpp = file:/MEMORY_OWNERS diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index e4ac8261afab..e38337540ad9 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -210,6 +210,7 @@ static struct { jfieldID lightTypePlayerId; jfieldID lightTypeKeyboardBacklight; jfieldID lightTypeKeyboardMicMute; + jfieldID lightTypeKeyboardVolumeMute; jfieldID lightCapabilityBrightness; jfieldID lightCapabilityColorRgb; } gLightClassInfo; @@ -2630,6 +2631,9 @@ static jobject nativeGetLights(JNIEnv* env, jobject nativeImplObj, jint deviceId } else if (lightInfo.type == InputDeviceLightType::KEYBOARD_MIC_MUTE) { jTypeId = env->GetStaticIntField(gLightClassInfo.clazz, gLightClassInfo.lightTypeKeyboardMicMute); + } else if (lightInfo.type == InputDeviceLightType::KEYBOARD_VOLUME_MUTE) { + jTypeId = env->GetStaticIntField(gLightClassInfo.clazz, + gLightClassInfo.lightTypeKeyboardVolumeMute); } else { ALOGW("Unknown light type %s", ftl::enum_string(lightInfo.type).c_str()); continue; @@ -3420,6 +3424,8 @@ int register_android_server_InputManager(JNIEnv* env) { env->GetStaticFieldID(gLightClassInfo.clazz, "LIGHT_TYPE_KEYBOARD_BACKLIGHT", "I"); gLightClassInfo.lightTypeKeyboardMicMute = env->GetStaticFieldID(gLightClassInfo.clazz, "LIGHT_TYPE_KEYBOARD_MIC_MUTE", "I"); + gLightClassInfo.lightTypeKeyboardVolumeMute = + env->GetStaticFieldID(gLightClassInfo.clazz, "LIGHT_TYPE_KEYBOARD_VOLUME_MUTE", "I"); gLightClassInfo.lightCapabilityBrightness = env->GetStaticFieldID(gLightClassInfo.clazz, "LIGHT_CAPABILITY_BRIGHTNESS", "I"); gLightClassInfo.lightCapabilityColorRgb = diff --git a/services/core/jni/com_android_server_utils_LazyJniRegistrar.cpp b/services/core/jni/com_android_server_utils_LazyJniRegistrar.cpp new file mode 100644 index 000000000000..ad7781e3b8b5 --- /dev/null +++ b/services/core/jni/com_android_server_utils_LazyJniRegistrar.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <nativehelper/JNIHelp.h> + +#include "jni.h" + +namespace android { + +// Forward declared per-class registration methods. +int register_android_server_ConsumerIrService(JNIEnv* env); +int register_android_server_vr_VrManagerService(JNIEnv* env); + +namespace { + +// TODO(b/)375264322: Remove these trampoline methods after finalizing the +// registrar implementation. Instead, just update the called methods to take a +// class arg, and hand those methods to jniRegisterNativeMethods directly. +void registerConsumerIrService(JNIEnv* env, jclass) { + register_android_server_ConsumerIrService(env); +} + +void registerVrManagerService(JNIEnv* env, jclass) { + register_android_server_vr_VrManagerService(env); +} + +static const JNINativeMethod sJniRegistrarMethods[] = { + {"registerConsumerIrService", "()V", (void*)registerConsumerIrService}, + {"registerVrManagerService", "()V", (void*)registerVrManagerService}, +}; + +} // namespace + +int register_android_server_utils_LazyJniRegistrar(JNIEnv* env) { + return jniRegisterNativeMethods(env, "com/android/server/utils/LazyJniRegistrar", + sJniRegistrarMethods, NELEM(sJniRegistrarMethods)); +} + +} // namespace android diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp index 59d7365d957d..c170ae99da04 100644 --- a/services/core/jni/onload.cpp +++ b/services/core/jni/onload.cpp @@ -25,7 +25,6 @@ namespace android { int register_android_server_BatteryStatsService(JNIEnv* env); -int register_android_server_ConsumerIrService(JNIEnv *env); int register_android_server_InputManager(JNIEnv* env); int register_android_server_LightsService(JNIEnv* env); int register_android_server_PowerManagerService(JNIEnv* env); @@ -38,7 +37,6 @@ int register_android_server_UsbAlsaJackDetector(JNIEnv* env); int register_android_server_UsbAlsaMidiDevice(JNIEnv* env); int register_android_server_UsbDeviceManager(JavaVM* vm, JNIEnv* env); int register_android_server_UsbHostManager(JNIEnv* env); -int register_android_server_vr_VrManagerService(JNIEnv* env); int register_android_server_vibrator_VibratorController(JavaVM* vm, JNIEnv* env); int register_android_server_vibrator_VibratorManagerService(JavaVM* vm, JNIEnv* env); int register_android_server_location_GnssLocationProvider(JNIEnv* env); @@ -56,6 +54,7 @@ int register_android_server_am_CachedAppOptimizer(JNIEnv* env); int register_android_server_am_Freezer(JNIEnv* env); int register_android_server_am_LowMemDetector(JNIEnv* env); int register_android_server_utils_AnrTimer(JNIEnv *env); +int register_android_server_utils_LazyJniRegistrar(JNIEnv* env); int register_com_android_server_soundtrigger_middleware_AudioSessionProviderImpl(JNIEnv* env); int register_com_android_server_soundtrigger_middleware_ExternalCaptureStateTracker(JNIEnv* env); int register_android_server_com_android_server_pm_PackageManagerShellCommandDataLoader(JNIEnv* env); @@ -72,6 +71,9 @@ int register_com_android_server_display_DisplayControl(JNIEnv* env); int register_com_android_server_SystemClockTime(JNIEnv* env); int register_android_server_display_smallAreaDetectionController(JNIEnv* env); int register_com_android_server_accessibility_BrailleDisplayConnection(JNIEnv* env); + +// Note: Consider adding new JNI entrypoints for optional services to +// LazyJniRegistrar instead, and relying on lazy registration. }; using namespace android; @@ -99,14 +101,12 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) register_android_server_UsbAlsaJackDetector(env); register_android_server_UsbAlsaMidiDevice(env); register_android_server_UsbHostManager(env); - register_android_server_vr_VrManagerService(env); register_android_server_vibrator_VibratorController(vm, env); register_android_server_vibrator_VibratorManagerService(vm, env); register_android_server_SystemServer(env); register_android_server_location_GnssLocationProvider(env); register_android_server_connectivity_Vpn(env); register_android_server_devicepolicy_CryptoTestHelper(env); - register_android_server_ConsumerIrService(env); register_android_server_BatteryStatsService(env); register_android_server_tv_TvUinputBridge(env); register_android_server_tv_TvInputHal(env); @@ -120,6 +120,7 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) register_android_server_am_Freezer(env); register_android_server_am_LowMemDetector(env); register_android_server_utils_AnrTimer(env); + register_android_server_utils_LazyJniRegistrar(env); register_com_android_server_soundtrigger_middleware_AudioSessionProviderImpl(env); register_com_android_server_soundtrigger_middleware_ExternalCaptureStateTracker(env); register_android_server_com_android_server_pm_PackageManagerShellCommandDataLoader(env); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java index 984105865f7d..c19c58e4ba13 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java @@ -99,10 +99,11 @@ import java.util.concurrent.CompletableFuture; final class DevicePolicyEngine { static final String TAG = "DevicePolicyEngine"; - // TODO(b/281701062): reference role name from role manager once its exposed. static final String DEVICE_LOCK_CONTROLLER_ROLE = "android.app.role.SYSTEM_FINANCED_DEVICE_CONTROLLER"; + static final String SYSTEM_SUPERVISION_ROLE = "android.app.role.SYSTEM_SUPERVISION"; + private static final String CELLULAR_2G_USER_RESTRICTION_ID = DevicePolicyIdentifiers.getIdentifierForUserRestriction( UserManager.DISALLOW_CELLULAR_2G); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index c6530381443f..d221e8ccb9b7 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -9068,35 +9068,14 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return; } - CallerIdentity caller; - if (Flags.setAutoTimeZoneEnabledCoexistence()) { - caller = getCallerIdentity(who, callerPackageName); - } else { - caller = getCallerIdentity(who); - } + CallerIdentity caller = getCallerIdentity(who); - if (Flags.setAutoTimeZoneEnabledCoexistence()) { - // The effect of this policy is device-wide. - EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin( - who, - SET_TIME_ZONE, - caller.getPackageName(), - UserHandle.USER_ALL - ); - mDevicePolicyEngine.setGlobalPolicy( - PolicyDefinition.AUTO_TIMEZONE, - // TODO(b/260573124): add correct enforcing admin when permission changes are - // merged. - enforcingAdmin, - new BooleanPolicyValue(enabled)); - } else { - Objects.requireNonNull(who, "ComponentName is null"); - Preconditions.checkCallAuthorization(isProfileOwnerOnUser0(caller) - || isProfileOwnerOfOrganizationOwnedDevice(caller) || isDefaultDeviceOwner( - caller)); - mInjector.binderWithCleanCallingIdentity(() -> - mInjector.settingsGlobalPutInt(Global.AUTO_TIME_ZONE, enabled ? 1 : 0)); - } + Objects.requireNonNull(who, "ComponentName is null"); + Preconditions.checkCallAuthorization(isProfileOwnerOnUser0(caller) + || isProfileOwnerOfOrganizationOwnedDevice(caller) || isDefaultDeviceOwner( + caller)); + mInjector.binderWithCleanCallingIdentity(() -> + mInjector.settingsGlobalPutInt(Global.AUTO_TIME_ZONE, enabled ? 1 : 0)); DevicePolicyEventLogger .createEvent(DevicePolicyEnums.SET_AUTO_TIME_ZONE) @@ -9114,24 +9093,68 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return false; } - CallerIdentity caller; - if (Flags.setAutoTimeZoneEnabledCoexistence()) { - caller = getCallerIdentity(who, callerPackageName); - } else { - caller = getCallerIdentity(who); + CallerIdentity caller = getCallerIdentity(who); + Objects.requireNonNull(who, "ComponentName is null"); + Preconditions.checkCallAuthorization(isProfileOwnerOnUser0(caller) + || isProfileOwnerOfOrganizationOwnedDevice(caller) || isDefaultDeviceOwner( + caller)); + return mInjector.settingsGlobalGetInt(Global.AUTO_TIME_ZONE, 0) > 0; + } + + /** + * Set auto time zone state. + */ + public void setAutoTimeZonePolicy(String callerPackageName, int policy) { + if (!mHasFeature) { + return; } - if (Flags.setAutoTimeZoneEnabledCoexistence()) { - // The effect of this policy is device-wide. - enforceCanQuery(SET_TIME_ZONE, caller.getPackageName(), UserHandle.USER_ALL); + CallerIdentity caller = getCallerIdentity(callerPackageName); + // The effect of this policy is device-wide. + EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin( + /* who */ null, + SET_TIME_ZONE, + caller.getPackageName(), + UserHandle.USER_ALL + ); + + if (policy != DevicePolicyManager.AUTO_TIME_ZONE_NOT_CONTROLLED_BY_POLICY) { + mDevicePolicyEngine.setGlobalPolicy( + PolicyDefinition.AUTO_TIME_ZONE, + enforcingAdmin, + new IntegerPolicyValue(policy)); + + DevicePolicyEventLogger + .createEvent(DevicePolicyEnums.SET_AUTO_TIME_ZONE) + .setAdmin(caller.getPackageName()) + .setBoolean(policy == DevicePolicyManager.AUTO_TIME_ZONE_ENABLED) + .write(); } else { - Objects.requireNonNull(who, "ComponentName is null"); - Preconditions.checkCallAuthorization(isProfileOwnerOnUser0(caller) - || isProfileOwnerOfOrganizationOwnedDevice(caller) || isDefaultDeviceOwner( - caller)); + mDevicePolicyEngine.removeGlobalPolicy( + PolicyDefinition.AUTO_TIME_ZONE, + enforcingAdmin); } + } - return mInjector.settingsGlobalGetInt(Global.AUTO_TIME_ZONE, 0) > 0; + /** + * Returns whether auto time zone is used on the device or not. + */ + @Override + public int getAutoTimeZonePolicy(String callerPackageName) { + if (!mHasFeature) { + return DevicePolicyManager.AUTO_TIME_ZONE_NOT_CONTROLLED_BY_POLICY; + } + CallerIdentity caller = getCallerIdentity(callerPackageName); + // The effect of this policy is device-wide. + EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin( + /* who */ null, + SET_TIME_ZONE, + caller.getPackageName(), + UserHandle.USER_ALL + ); + Integer state = mDevicePolicyEngine.getGlobalPolicySetByAdmin( + PolicyDefinition.AUTO_TIME_ZONE, enforcingAdmin); + return state != null ? state : DevicePolicyManager.AUTO_TIME_ZONE_NOT_CONTROLLED_BY_POLICY; } // TODO (b/137101239): remove this method in follow-up CL @@ -21030,6 +21053,27 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } @Override + public boolean removeManagedProfile(int userId) { + Preconditions.checkCallAuthorization( + hasCallingOrSelfPermission(MANAGE_PROFILE_AND_DEVICE_OWNERS)); + + if (!isManagedProfile(userId)){ + throw new IllegalArgumentException("Cannot remove user as it is not a managed profile"); + } + + boolean success = false; + final long identity = Binder.clearCallingIdentity(); + try{ + success = mUserManager.removeUserEvenWhenDisallowed(userId); + } catch (Exception e) { + Slogf.e(LOG_TAG, "Remove managed profile failed due to: ", e); + } finally { + Binder.restoreCallingIdentity(identity); + } + return success; + } + + @Override public UserHandle createAndProvisionManagedProfile( @NonNull ManagedProfileProvisioningParams provisioningParams, @NonNull String callerPackage) { @@ -23765,9 +23809,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { Slogf.i(LOG_TAG, "Started device policies migration to the device policy engine."); // TODO(b/359188869): Move this to the current migration method. - if (Flags.setAutoTimeZoneEnabledCoexistence()) { - migrateAutoTimezonePolicy(); - } if (Flags.setPermissionGrantStateCoexistence()) { migratePermissionGrantStatePolicies(); } @@ -23816,11 +23857,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { // Additional migration steps should repeat the pattern above with a new backupId. } - private void migrateAutoTimezonePolicy() { - Slogf.i(LOG_TAG, "Skipping Migration of AUTO_TIMEZONE policy to device policy engine," - + "as no way to identify if the value was set by the admin or the user."); - } - 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/PolicyDefinition.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java index f1711f5f8c0b..a5aeaace94bc 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java @@ -17,6 +17,7 @@ package com.android.server.devicepolicy; import static com.android.server.devicepolicy.DevicePolicyEngine.DEVICE_LOCK_CONTROLLER_ROLE; +import static com.android.server.devicepolicy.DevicePolicyEngine.SYSTEM_SUPERVISION_ROLE; import android.annotation.NonNull; import android.annotation.Nullable; @@ -94,14 +95,18 @@ final class PolicyDefinition<V> { private static final MostRestrictive<Boolean> TRUE_MORE_RESTRICTIVE = new MostRestrictive<>( List.of(new BooleanPolicyValue(true), new BooleanPolicyValue(false))); - static PolicyDefinition<Boolean> AUTO_TIMEZONE = new PolicyDefinition<>( + static PolicyDefinition<Integer> AUTO_TIME_ZONE = new PolicyDefinition<>( new NoArgsPolicyKey(DevicePolicyIdentifiers.AUTO_TIMEZONE_POLICY), - // auto timezone is disabled by default, hence enabling it is more restrictive. - TRUE_MORE_RESTRICTIVE, + // Auto time zone is enabled by default. Enabled state has higher priority given it + // means the time will be more precise and other applications can rely on that for + // their purposes. + new TopPriority<>(List.of( + EnforcingAdmin.getRoleAuthorityOf(SYSTEM_SUPERVISION_ROLE), + EnforcingAdmin.getRoleAuthorityOf(DEVICE_LOCK_CONTROLLER_ROLE), + EnforcingAdmin.DPC_AUTHORITY)), POLICY_FLAG_GLOBAL_ONLY_POLICY, - (Boolean value, Context context, Integer userId, PolicyKey policyKey) -> - PolicyEnforcerCallbacks.setAutoTimezoneEnabled(value, context), - new BooleanPolicySerializer()); + PolicyEnforcerCallbacks::setAutoTimeZonePolicy, + new IntegerPolicySerializer()); static final PolicyDefinition<Integer> GENERIC_PERMISSION_GRANT = new PolicyDefinition<>( @@ -349,7 +354,7 @@ final class PolicyDefinition<V> { // TODO(b/277218360): Revisit policies that should be marked as global-only. static { - POLICY_DEFINITIONS.put(DevicePolicyIdentifiers.AUTO_TIMEZONE_POLICY, AUTO_TIMEZONE); + POLICY_DEFINITIONS.put(DevicePolicyIdentifiers.AUTO_TIMEZONE_POLICY, AUTO_TIME_ZONE); POLICY_DEFINITIONS.put(DevicePolicyIdentifiers.PERMISSION_GRANT_POLICY, GENERIC_PERMISSION_GRANT); POLICY_DEFINITIONS.put(DevicePolicyIdentifiers.SECURITY_LOGGING_POLICY, diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java index fdc0ec1a0471..40d8dae41dcf 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java @@ -81,20 +81,24 @@ final class PolicyEnforcerCallbacks { return AndroidFuture.completedFuture(true); } - static CompletableFuture<Boolean> setAutoTimezoneEnabled(@Nullable Boolean enabled, - @NonNull Context context) { + static CompletableFuture<Boolean> setAutoTimeZonePolicy( + @Nullable Integer policy, @NonNull Context context, int userId, + @NonNull PolicyKey policyKey) { if (!Flags.setAutoTimeZoneEnabledCoexistence()) { - Slogf.w(LOG_TAG, "Trying to enforce setAutoTimezoneEnabled while flag is off."); + Slogf.w(LOG_TAG, "Trying to enforce setAutoTimeZonePolicy while flag is off."); return AndroidFuture.completedFuture(true); } return Binder.withCleanCallingIdentity(() -> { Objects.requireNonNull(context); - - int value = enabled != null && enabled ? 1 : 0; - return AndroidFuture.completedFuture( - Settings.Global.putInt( - context.getContentResolver(), Settings.Global.AUTO_TIME_ZONE, - value)); + if (policy != null && + policy == DevicePolicyManager.AUTO_TIME_ZONE_NOT_CONTROLLED_BY_POLICY) { + return AndroidFuture.completedFuture(false); + } + int enabled = policy != null && + policy == DevicePolicyManager.AUTO_TIME_ZONE_ENABLED ? 1 : 0; + return AndroidFuture.completedFuture(Settings.Global.putInt( + context.getContentResolver(), Settings.Global.AUTO_TIME_ZONE, + enabled)); }); } diff --git a/services/foldables/devicestateprovider/src/com/android/server/policy/BookStyleDeviceStatePolicy.java b/services/foldables/devicestateprovider/src/com/android/server/policy/BookStyleDeviceStatePolicy.java index cc5573bb01d8..f34ec72d7e27 100644 --- a/services/foldables/devicestateprovider/src/com/android/server/policy/BookStyleDeviceStatePolicy.java +++ b/services/foldables/devicestateprovider/src/com/android/server/policy/BookStyleDeviceStatePolicy.java @@ -19,6 +19,7 @@ package com.android.server.policy; import static android.hardware.devicestate.DeviceState.PROPERTY_EMULATED_ONLY; import static android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT; import static android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_REAR_DISPLAY; +import static android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_REAR_DISPLAY_OUTER_DEFAULT; import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY; import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY; import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED; @@ -71,6 +72,7 @@ public class BookStyleDeviceStatePolicy extends DeviceStatePolicy implements private static final int DEVICE_STATE_OPENED = 2; private static final int DEVICE_STATE_REAR_DISPLAY = 3; private static final int DEVICE_STATE_CONCURRENT_INNER_DEFAULT = 4; + private static final int DEVICE_STATE_REAR_DISPLAY_OUTER_DEFAULT = 5; private static final int TENT_MODE_SWITCH_ANGLE_DEGREES = 90; private static final int TABLE_TOP_MODE_SWITCH_ANGLE_DEGREES = 125; private static final int MIN_CLOSED_ANGLE_DEGREES = 0; @@ -130,14 +132,17 @@ public class BookStyleDeviceStatePolicy extends DeviceStatePolicy implements return hingeAngle >= MAX_CLOSED_ANGLE_DEGREES && hingeAngle <= TABLE_TOP_MODE_SWITCH_ANGLE_DEGREES; }), - createConfig(getOpenedDeviceState(), /* activeStatePredicate= */ - ALLOWED), - createConfig(getRearDisplayDeviceState(), /* activeStatePredicate= */ - NOT_ALLOWED), - createConfig(getDualDisplayDeviceState(), /* activeStatePredicate= */ - NOT_ALLOWED, /* availabilityPredicate= */ - provider -> !mIsDualDisplayBlockingEnabled - || provider.hasNoConnectedExternalDisplay())}; + createConfig(getOpenedDeviceState(), + /* activeStatePredicate= */ ALLOWED), + createConfig(getRearDisplayDeviceState(), + /* activeStatePredicate= */ NOT_ALLOWED), + createConfig(getDualDisplayDeviceState(), + /* activeStatePredicate= */ NOT_ALLOWED, + /* availabilityPredicate= */ provider -> !mIsDualDisplayBlockingEnabled + || provider.hasNoConnectedExternalDisplay()), + createConfig(getRearDisplayOuterDefaultState(), + /* activeStatePredicate= */ NOT_ALLOWED) + }; } private DeviceStatePredicateWrapper createClosedConfiguration( @@ -266,4 +271,24 @@ public class BookStyleDeviceStatePolicy extends DeviceStatePolicy implements .setSystemProperties(systemProperties) .build()); } + + /** + * Returns the {link DeviceState.Configuration} that represents the new rear display state + * where the inner display is also enabled, showing a system affordance to exit the state. + */ + @NonNull + private DeviceState getRearDisplayOuterDefaultState() { + Set<@DeviceState.SystemDeviceStateProperties Integer> systemProperties = new HashSet<>( + List.of(PROPERTY_EMULATED_ONLY, + PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY, + PROPERTY_POLICY_AVAILABLE_FOR_APP_REQUEST, + PROPERTY_FEATURE_REAR_DISPLAY, + PROPERTY_FEATURE_REAR_DISPLAY_OUTER_DEFAULT)); + + return new DeviceState(new DeviceState.Configuration.Builder( + DEVICE_STATE_REAR_DISPLAY_OUTER_DEFAULT, + "REAR_DISPLAY_OUTER_DEFAULT") + .setSystemProperties(systemProperties) + .build()); + } } diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 3805c02d1bb9..19b03437292f 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -119,7 +119,6 @@ import com.android.internal.widget.ILockSettings; import com.android.internal.widget.LockSettingsInternal; import com.android.server.accessibility.AccessibilityManagerService; import com.android.server.accounts.AccountManagerService; -import com.android.server.adaptiveauth.AdaptiveAuthService; import com.android.server.adb.AdbService; import com.android.server.alarm.AlarmManagerService; import com.android.server.am.ActivityManagerService; @@ -205,6 +204,7 @@ import com.android.server.os.BugreportManagerService; import com.android.server.os.DeviceIdentifiersPolicyService; import com.android.server.os.NativeTombstoneManagerService; import com.android.server.os.SchedulingPolicyService; +import com.android.server.os.instrumentation.DynamicInstrumentationManagerService; import com.android.server.pdb.PersistentDataBlockService; import com.android.server.people.PeopleService; import com.android.server.permission.access.AccessCheckingService; @@ -249,6 +249,7 @@ import com.android.server.security.AttestationVerificationManagerService; import com.android.server.security.FileIntegrityService; import com.android.server.security.KeyAttestationApplicationIdProviderService; import com.android.server.security.KeyChainSystemService; +import com.android.server.security.adaptiveauthentication.AdaptiveAuthenticationService; import com.android.server.security.advancedprotection.AdvancedProtectionService; import com.android.server.security.rkp.RemoteProvisioningService; import com.android.server.selinux.SelinuxAuditLogsService; @@ -1620,7 +1621,8 @@ public final class SystemServer implements Dumpable { mSystemServiceManager.startService(ROLE_SERVICE_CLASS); t.traceEnd(); - if (!isWatch && android.app.supervision.flags.Flags.supervisionApi()) { + if (android.app.supervision.flags.Flags.supervisionApi() + && (!isWatch || android.app.supervision.flags.Flags.supervisionApiOnWear())) { t.traceBegin("StartSupervisionService"); mSystemServiceManager.startService(SupervisionService.Lifecycle.class); t.traceEnd(); @@ -2650,8 +2652,8 @@ public final class SystemServer implements Dumpable { t.traceEnd(); if (android.adaptiveauth.Flags.enableAdaptiveAuth()) { - t.traceBegin("StartAdaptiveAuthService"); - mSystemServiceManager.startService(AdaptiveAuthService.class); + t.traceBegin("StartAdaptiveAuthenticationService"); + mSystemServiceManager.startService(AdaptiveAuthenticationService.class); t.traceEnd(); } @@ -2890,6 +2892,13 @@ public final class SystemServer implements Dumpable { mSystemServiceManager.startService(TracingServiceProxy.class); t.traceEnd(); + // UprobeStats DynamicInstrumentationManager + if (com.android.art.flags.Flags.executableMethodFileOffsets()) { + t.traceBegin("StartDynamicInstrumentationManager"); + mSystemServiceManager.startService(DynamicInstrumentationManagerService.class); + t.traceEnd(); + } + // It is now time to start up the app processes... t.traceBegin("MakeLockSettingsServiceReady"); diff --git a/services/supervision/java/com/android/server/supervision/SupervisionManagerInternal.java b/services/supervision/java/com/android/server/supervision/SupervisionManagerInternal.java index fead05bc7e49..5df9dd521092 100644 --- a/services/supervision/java/com/android/server/supervision/SupervisionManagerInternal.java +++ b/services/supervision/java/com/android/server/supervision/SupervisionManagerInternal.java @@ -35,6 +35,14 @@ public abstract class SupervisionManagerInternal { public abstract boolean isSupervisionEnabledForUser(@UserIdInt int userId); /** + * Set whether supervision is enabled for the specified user. + * + * @param userId The user to set the supervision state for + * @param enabled Whether or not the user should be supervised + */ + public abstract void setSupervisionEnabledForUser(@UserIdInt int userId, boolean enabled); + + /** * Sets whether the supervision lock screen should be shown for the specified user * * @param userId The user set the superivision state for diff --git a/services/supervision/java/com/android/server/supervision/SupervisionService.java b/services/supervision/java/com/android/server/supervision/SupervisionService.java index 4c515c173c8d..67e254782f6d 100644 --- a/services/supervision/java/com/android/server/supervision/SupervisionService.java +++ b/services/supervision/java/com/android/server/supervision/SupervisionService.java @@ -19,7 +19,9 @@ package com.android.server.supervision; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; +import android.app.admin.DevicePolicyManagerInternal; import android.app.supervision.ISupervisionManager; +import android.content.ComponentName; import android.content.Context; import android.content.pm.UserInfo; import android.os.Bundle; @@ -28,19 +30,19 @@ import android.os.ResultReceiver; import android.os.ShellCallback; import android.util.SparseArray; +import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.DumpUtils; import com.android.internal.util.IndentingPrintWriter; import com.android.server.LocalServices; import com.android.server.SystemService; +import com.android.server.SystemService.TargetUser; import com.android.server.pm.UserManagerInternal; import java.io.FileDescriptor; import java.io.PrintWriter; -/** - * Service for handling system supervision. - */ +/** Service for handling system supervision. */ public class SupervisionService extends ISupervisionManager.Stub { private static final String LOG_TAG = "SupervisionService"; @@ -52,14 +54,25 @@ public class SupervisionService extends ISupervisionManager.Stub { @GuardedBy("getLockObject()") private final SparseArray<SupervisionUserData> mUserData = new SparseArray<>(); + private final DevicePolicyManagerInternal mDpmInternal; private final UserManagerInternal mUserManagerInternal; public SupervisionService(Context context) { mContext = context.createAttributionContext(LOG_TAG); + mDpmInternal = LocalServices.getService(DevicePolicyManagerInternal.class); mUserManagerInternal = LocalServices.getService(UserManagerInternal.class); mUserManagerInternal.addUserLifecycleListener(new UserLifecycleListener()); } + void syncStateWithDevicePolicyManager(TargetUser user) { + if (user.isPreCreated()) return; + + // Ensure that supervision is enabled when supervision app is the profile owner. + if (android.app.admin.flags.Flags.enableSupervisionServiceSync() && isProfileOwner(user)) { + setSupervisionEnabledForUser(user.getUserIdentifier(), true); + } + } + @Override public boolean isSupervisionEnabledForUser(@UserIdInt int userId) { synchronized (getLockObject()) { @@ -74,14 +87,15 @@ public class SupervisionService extends ISupervisionManager.Stub { @Nullable FileDescriptor err, @NonNull String[] args, @Nullable ShellCallback callback, - @NonNull ResultReceiver resultReceiver) throws RemoteException { + @NonNull ResultReceiver resultReceiver) + throws RemoteException { new SupervisionServiceShellCommand(this) .exec(this, in, out, err, args, callback, resultReceiver); } @Override - protected void dump(@NonNull FileDescriptor fd, - @NonNull PrintWriter printWriter, @Nullable String[] args) { + protected void dump( + @NonNull FileDescriptor fd, @NonNull PrintWriter printWriter, @Nullable String[] args) { if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, printWriter)) return; try (var pw = new IndentingPrintWriter(printWriter, " ")) { @@ -120,6 +134,17 @@ public class SupervisionService extends ISupervisionManager.Stub { } } + /** Returns whether the supervision app has profile owner status. */ + private boolean isProfileOwner(TargetUser user) { + ComponentName profileOwner = mDpmInternal.getProfileOwnerAsUser(user.getUserIdentifier()); + if (profileOwner == null) { + return false; + } + + String configPackage = mContext.getResources().getString(R.string.config_systemSupervision); + return profileOwner.getPackageName().equals(configPackage); + } + public static class Lifecycle extends SystemService { private final SupervisionService mSupervisionService; @@ -133,13 +158,24 @@ public class SupervisionService extends ISupervisionManager.Stub { publishLocalService(SupervisionManagerInternal.class, mSupervisionService.mInternal); publishBinderService(Context.SUPERVISION_SERVICE, mSupervisionService); } + + @Override + public void onUserStarting(@NonNull TargetUser user) { + mSupervisionService.syncStateWithDevicePolicyManager(user); + } } - final SupervisionManagerInternal mInternal = new SupervisionManagerInternal() { + final SupervisionManagerInternal mInternal = new SupervisionManagerInternalImpl(); + + private final class SupervisionManagerInternalImpl extends SupervisionManagerInternal { + @Override public boolean isSupervisionEnabledForUser(@UserIdInt int userId) { - synchronized (getLockObject()) { - return getUserDataLocked(userId).supervisionEnabled; - } + return SupervisionService.this.isSupervisionEnabledForUser(userId); + } + + @Override + public void setSupervisionEnabledForUser(@UserIdInt int userId, boolean enabled) { + SupervisionService.this.setSupervisionEnabledForUser(userId, enabled); } @Override @@ -151,7 +187,7 @@ public class SupervisionService extends ISupervisionManager.Stub { data.supervisionLockScreenOptions = options; } } - }; + } private final class UserLifecycleListener implements UserManagerInternal.UserLifecycleListener { @Override diff --git a/services/tests/DynamicInstrumentationManagerServiceTests/Android.bp b/services/tests/DynamicInstrumentationManagerServiceTests/Android.bp new file mode 100644 index 000000000000..2c2e5fdb68d9 --- /dev/null +++ b/services/tests/DynamicInstrumentationManagerServiceTests/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_system_performance", +} + +android_test { + name: "DynamicInstrumentationManagerServiceTests", + srcs: ["src/**/*.java"], + + static_libs: [ + "androidx.test.core", + "androidx.test.runner", + "hamcrest-library", + "platform-test-annotations", + "services.core", + "testables", + "truth", + ], + + certificate: "platform", + platform_apis: true, + test_suites: [ + "device-tests", + ], +} diff --git a/services/tests/DynamicInstrumentationManagerServiceTests/AndroidManifest.xml b/services/tests/DynamicInstrumentationManagerServiceTests/AndroidManifest.xml new file mode 100644 index 000000000000..4913d706a72d --- /dev/null +++ b/services/tests/DynamicInstrumentationManagerServiceTests/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.server.os.instrumentation" > + + <application> + <uses-library android:name="android.test.runner" /> + </application> + + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.server.os.instrumentation" + android:label="DynamicInstrumentationmanagerService Unit Tests"/> +</manifest>
\ No newline at end of file diff --git a/services/tests/DynamicInstrumentationManagerServiceTests/TEST_MAPPING b/services/tests/DynamicInstrumentationManagerServiceTests/TEST_MAPPING new file mode 100644 index 000000000000..33defed0eae6 --- /dev/null +++ b/services/tests/DynamicInstrumentationManagerServiceTests/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "postsubmit": [ + { + "name": "DynamicInstrumentationManagerServiceTests" + } + ] +} diff --git a/services/tests/DynamicInstrumentationManagerServiceTests/src/com/android/server/TestAbstractClass.java b/services/tests/DynamicInstrumentationManagerServiceTests/src/com/android/server/TestAbstractClass.java new file mode 100644 index 000000000000..04073fab2059 --- /dev/null +++ b/services/tests/DynamicInstrumentationManagerServiceTests/src/com/android/server/TestAbstractClass.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +public abstract class TestAbstractClass { + abstract void abstractMethod(); + + void concreteMethod() { + } +} diff --git a/services/tests/DynamicInstrumentationManagerServiceTests/src/com/android/server/TestAbstractClassImpl.java b/services/tests/DynamicInstrumentationManagerServiceTests/src/com/android/server/TestAbstractClassImpl.java new file mode 100644 index 000000000000..2c25e7a52f73 --- /dev/null +++ b/services/tests/DynamicInstrumentationManagerServiceTests/src/com/android/server/TestAbstractClassImpl.java @@ -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.server; + +public class TestAbstractClassImpl extends TestAbstractClass { + @Override + void abstractMethod() { + } +} diff --git a/services/tests/DynamicInstrumentationManagerServiceTests/src/com/android/server/TestClass.java b/services/tests/DynamicInstrumentationManagerServiceTests/src/com/android/server/TestClass.java new file mode 100644 index 000000000000..085f5953f0e5 --- /dev/null +++ b/services/tests/DynamicInstrumentationManagerServiceTests/src/com/android/server/TestClass.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.server; + +public class TestClass { + void primitiveParams(boolean a, boolean[] b, byte c, byte[] d, char e, char[] f, short g, + short[] h, int i, int[] j, long k, long[] l, float m, float[] n, double o, double[] p) { + } + + void classParams(String a, String[] b) { + } + + private void privateMethod() { + } + + /** + * docs! + */ + public void publicMethod() { + } + + private static class InnerClass { + private void innerMethod(InnerClass arg, InnerClass[] argArray) { + } + } +} diff --git a/services/tests/DynamicInstrumentationManagerServiceTests/src/com/android/server/TestInterface.java b/services/tests/DynamicInstrumentationManagerServiceTests/src/com/android/server/TestInterface.java new file mode 100644 index 000000000000..7af4f254ab1c --- /dev/null +++ b/services/tests/DynamicInstrumentationManagerServiceTests/src/com/android/server/TestInterface.java @@ -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.server; + +/** + * docs! + */ +public interface TestInterface { + /** + * docs! + */ + void interfaceMethod(); + + /** + * docs! + */ + default void defaultMethod() { + } +} diff --git a/services/tests/DynamicInstrumentationManagerServiceTests/src/com/android/server/TestInterfaceImpl.java b/services/tests/DynamicInstrumentationManagerServiceTests/src/com/android/server/TestInterfaceImpl.java new file mode 100644 index 000000000000..53aecbc08939 --- /dev/null +++ b/services/tests/DynamicInstrumentationManagerServiceTests/src/com/android/server/TestInterfaceImpl.java @@ -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.server; + +public class TestInterfaceImpl implements TestInterface { + @Override + public void interfaceMethod() { + } +} diff --git a/services/tests/DynamicInstrumentationManagerServiceTests/src/com/android/server/os/instrumentation/ParseMethodDescriptorTest.java b/services/tests/DynamicInstrumentationManagerServiceTests/src/com/android/server/os/instrumentation/ParseMethodDescriptorTest.java new file mode 100644 index 000000000000..5492ba6b9dd1 --- /dev/null +++ b/services/tests/DynamicInstrumentationManagerServiceTests/src/com/android/server/os/instrumentation/ParseMethodDescriptorTest.java @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.os.instrumentation; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThrows; + +import android.os.instrumentation.MethodDescriptor; +import android.platform.test.annotations.Presubmit; + +import androidx.test.filters.SmallTest; + +import com.android.server.TestAbstractClass; +import com.android.server.TestAbstractClassImpl; +import com.android.server.TestClass; +import com.android.server.TestInterface; +import com.android.server.TestInterfaceImpl; + +import org.junit.Test; + +import java.lang.reflect.Method; + + +/** + * Test class for + * {@link DynamicInstrumentationManagerService#parseMethodDescriptor(ClassLoader, + * MethodDescriptor)}. + * <p> + * Build/Install/Run: + * atest FrameworksMockingServicesTests:ParseMethodDescriptorTest + */ +@Presubmit +@SmallTest +public class ParseMethodDescriptorTest { + private static final String[] PRIMITIVE_PARAMS = new String[]{ + "boolean", "boolean[]", "byte", "byte[]", "char", "char[]", "short", "short[]", "int", + "int[]", "long", "long[]", "float", "float[]", "double", "double[]"}; + private static final String[] CLASS_PARAMS = + new String[]{"java.lang.String", "java.lang.String[]"}; + + @Test + public void primitiveParams() { + assertNotNull(parseMethodDescriptor(TestClass.class.getName(), "primitiveParams", + PRIMITIVE_PARAMS)); + } + + @Test + public void classParams() { + assertNotNull( + parseMethodDescriptor(TestClass.class.getName(), "classParams", CLASS_PARAMS)); + } + + @Test + public void publicMethod() { + assertNotNull( + parseMethodDescriptor(TestClass.class.getName(), "publicMethod")); + } + + @Test + public void privateMethod() { + assertNotNull( + parseMethodDescriptor(TestClass.class.getName(), "privateMethod")); + } + + @Test + public void innerClass() { + assertNotNull( + parseMethodDescriptor(TestClass.class.getName() + "$InnerClass", "innerMethod", + new String[]{TestClass.class.getName() + "$InnerClass", + TestClass.class.getName() + "$InnerClass[]"})); + } + + @Test + public void interface_concreteMethod() { + assertNotNull( + parseMethodDescriptor(TestInterfaceImpl.class.getName(), "interfaceMethod")); + } + + @Test + public void interface_defaultMethod() { + assertNotNull( + parseMethodDescriptor(TestInterface.class.getName(), "defaultMethod")); + } + + @Test + public void abstractClassImpl_abstractMethod() { + assertNotNull( + parseMethodDescriptor(TestAbstractClassImpl.class.getName(), "abstractMethod")); + } + + @Test + public void abstractClass_concreteMethod() { + assertNotNull( + parseMethodDescriptor(TestAbstractClass.class.getName(), "concreteMethod")); + } + + @Test + public void notFound_illegalArgumentException() { + assertThrows(IllegalArgumentException.class, () -> parseMethodDescriptor("foo", "bar")); + assertThrows(IllegalArgumentException.class, + () -> parseMethodDescriptor(TestClass.class.getName(), "bar")); + assertThrows(IllegalArgumentException.class, + () -> parseMethodDescriptor(TestClass.class.getName(), "primitiveParams", + new String[]{"int"})); + } + + private Method parseMethodDescriptor(String fqcn, String methodName) { + return DynamicInstrumentationManagerService.parseMethodDescriptor( + getClass().getClassLoader(), + getMethodDescriptor(fqcn, methodName, new String[]{})); + } + + private Method parseMethodDescriptor(String fqcn, String methodName, String[] fqParameters) { + return DynamicInstrumentationManagerService.parseMethodDescriptor( + getClass().getClassLoader(), + getMethodDescriptor(fqcn, methodName, fqParameters)); + } + + private MethodDescriptor getMethodDescriptor(String fqcn, String methodName, + String[] fqParameters) { + MethodDescriptor methodDescriptor = new MethodDescriptor(); + methodDescriptor.fullyQualifiedClassName = fqcn; + methodDescriptor.methodName = methodName; + methodDescriptor.fullyQualifiedParameters = fqParameters; + return methodDescriptor; + } + + +} diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/BroadcastHelperTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/BroadcastHelperTest.java new file mode 100644 index 000000000000..1be5cef28244 --- /dev/null +++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/BroadcastHelperTest.java @@ -0,0 +1,195 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm; + +import static android.content.pm.Flags.FLAG_REDUCE_BROADCASTS_FOR_COMPONENT_STATE_CHANGES; + +import static com.google.common.truth.Truth.assertThat; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.ActivityManager; +import android.app.ActivityManagerInternal; +import android.content.Context; +import android.content.Intent; +import android.os.Handler; +import android.os.Message; +import android.os.UserHandle; +import android.platform.test.annotations.AppModeFull; +import android.platform.test.annotations.AppModeNonSdkSandbox; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.platform.app.InstrumentationRegistry; + +import com.android.internal.pm.parsing.pkg.AndroidPackageInternal; +import com.android.internal.pm.pkg.component.ParsedActivity; +import com.android.server.pm.pkg.PackageStateInternal; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.ArrayList; + +@AppModeFull +@AppModeNonSdkSandbox +@RunWith(AndroidJUnit4.class) +public class BroadcastHelperTest { + private static final String TAG = "BroadcastHelperTest"; + private static final String PACKAGE_CHANGED_TEST_PACKAGE_NAME = "testpackagename"; + private static final String PACKAGE_CHANGED_TEST_MAIN_ACTIVITY = + PACKAGE_CHANGED_TEST_PACKAGE_NAME + ".MainActivity"; + private static final String PERMISSION_PACKAGE_CHANGED_BROADCAST_ON_COMPONENT_STATE_CHANGED = + "android.permission.INTERNAL_RECEIVE_PACKAGE_CHANGED_BROADCAST_ON_COMPONENT_STATE_CHANGED"; + + @Rule + public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); + + @Mock + ActivityManagerInternal mMockActivityManagerInternal; + @Mock + AndroidPackageInternal mMockAndroidPackageInternal; + @Mock + Computer mMockSnapshot; + @Mock + Handler mMockHandler; + @Mock + PackageManagerServiceInjector mMockPackageManagerServiceInjector; + @Mock + PackageMonitorCallbackHelper mMockPackageMonitorCallbackHelper; + @Mock + PackageStateInternal mMockPackageStateInternal; + @Mock + ParsedActivity mMockParsedActivity; + @Mock + UserManagerInternal mMockUserManagerInternal; + + private Context mContext; + private BroadcastHelper mBroadcastHelper; + + @Before + public void setup() throws Exception { + MockitoAnnotations.initMocks(this); + + mContext = InstrumentationRegistry.getInstrumentation().getContext(); + + when(mMockHandler.sendMessageAtTime(any(Message.class), anyLong())).thenAnswer( + i -> { + ((Message) i.getArguments()[0]).getCallback().run(); + return true; + }); + when(mMockPackageManagerServiceInjector.getActivityManagerInternal()).thenReturn( + mMockActivityManagerInternal); + when(mMockPackageManagerServiceInjector.getContext()).thenReturn(mContext); + when(mMockPackageManagerServiceInjector.getHandler()).thenReturn(mMockHandler); + when(mMockPackageManagerServiceInjector.getPackageMonitorCallbackHelper()).thenReturn( + mMockPackageMonitorCallbackHelper); + when(mMockPackageManagerServiceInjector.getUserManagerInternal()).thenReturn( + mMockUserManagerInternal); + + mBroadcastHelper = new BroadcastHelper(mMockPackageManagerServiceInjector); + } + + @RequiresFlagsEnabled(FLAG_REDUCE_BROADCASTS_FOR_COMPONENT_STATE_CHANGES) + @Test + public void changeNonExportedComponent_sendPackageChangedBroadcastToSystem_withPermission() + throws Exception { + changeComponentAndSendPackageChangedBroadcast(false /* changeExportedComponent */); + + ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class); + verify(mMockActivityManagerInternal).broadcastIntentWithCallback( + captor.capture(), eq(null), + eq(new String[]{PERMISSION_PACKAGE_CHANGED_BROADCAST_ON_COMPONENT_STATE_CHANGED}), + anyInt(), eq(null), eq(null), eq(null)); + Intent intent = captor.getValue(); + assertNotNull(intent); + assertThat(intent.getPackage()).isEqualTo("android"); + } + + @RequiresFlagsEnabled(FLAG_REDUCE_BROADCASTS_FOR_COMPONENT_STATE_CHANGES) + @Test + public void changeNonExportedComponent_sendPackageChangedBroadcastToApplicationItself() + throws Exception { + changeComponentAndSendPackageChangedBroadcast(false /* changeExportedComponent */); + + ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class); + verify(mMockActivityManagerInternal).broadcastIntentWithCallback(captor.capture(), eq(null), + eq(null), anyInt(), eq(null), eq(null), eq(null)); + Intent intent = captor.getValue(); + assertNotNull(intent); + assertThat(intent.getPackage()).isEqualTo(PACKAGE_CHANGED_TEST_PACKAGE_NAME); + } + + @Test + public void changeExportedComponent_sendPackageChangedBroadcastToAll() throws Exception { + changeComponentAndSendPackageChangedBroadcast(true /* changeExportedComponent */); + + ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class); + verify(mMockActivityManagerInternal).broadcastIntentWithCallback(captor.capture(), eq(null), + eq(null), anyInt(), eq(null), eq(null), eq(null)); + Intent intent = captor.getValue(); + assertNotNull(intent); + assertNull(intent.getPackage()); + } + + private void changeComponentAndSendPackageChangedBroadcast(boolean changeExportedComponent) { + when(mMockSnapshot.getPackageStateInternal(eq(PACKAGE_CHANGED_TEST_PACKAGE_NAME), + anyInt())).thenReturn(mMockPackageStateInternal); + when(mMockSnapshot.isInstantAppInternal(any(), anyInt(), anyInt())).thenReturn(false); + when(mMockSnapshot.getVisibilityAllowLists(any(), any())).thenReturn(null); + when(mMockPackageStateInternal.getPkg()).thenReturn(mMockAndroidPackageInternal); + + when(mMockParsedActivity.getClassName()).thenReturn( + PACKAGE_CHANGED_TEST_MAIN_ACTIVITY); + when(mMockParsedActivity.isExported()).thenReturn(changeExportedComponent); + ArrayList<ParsedActivity> parsedActivities = new ArrayList<>(); + parsedActivities.add(mMockParsedActivity); + + when(mMockAndroidPackageInternal.getReceivers()).thenReturn(new ArrayList<>()); + when(mMockAndroidPackageInternal.getProviders()).thenReturn(new ArrayList<>()); + when(mMockAndroidPackageInternal.getServices()).thenReturn(new ArrayList<>()); + when(mMockAndroidPackageInternal.getActivities()).thenReturn(parsedActivities); + + doNothing().when(mMockPackageMonitorCallbackHelper).notifyPackageChanged(any(), + anyBoolean(), any(), anyInt(), any(), any(), any(), any(), any()); + when(mMockActivityManagerInternal.broadcastIntentWithCallback(any(), any(), any(), anyInt(), + any(), any(), any())).thenReturn(ActivityManager.BROADCAST_SUCCESS); + + ArrayList<String> componentNames = new ArrayList<>(); + componentNames.add(PACKAGE_CHANGED_TEST_MAIN_ACTIVITY); + + mBroadcastHelper.sendPackageChangedBroadcast(mMockSnapshot, + PACKAGE_CHANGED_TEST_PACKAGE_NAME, true /* dontKillApp */, componentNames, + UserHandle.USER_SYSTEM, "test" /* reason */); + } +} diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageInstallerSessionTest.kt b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageInstallerSessionTest.kt index 09d0e4a82f7f..5a59c57ddf28 100644 --- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageInstallerSessionTest.kt +++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageInstallerSessionTest.kt @@ -201,7 +201,8 @@ class PackageInstallerSessionTest { /* preVerifiedDomains */ DomainSet(setOf("com.foo", "com.bar")), /* VerifierController */ mock(VerifierController::class.java), /* initialVerificationPolicy */ VERIFICATION_POLICY_BLOCK_FAIL_OPEN, - /* currentVerificationPolicy */ VERIFICATION_POLICY_BLOCK_FAIL_CLOSED + /* currentVerificationPolicy */ VERIFICATION_POLICY_BLOCK_FAIL_CLOSED, + /* installDependencyHelper */ null ) } @@ -256,7 +257,8 @@ class PackageInstallerSessionTest { mTmpDir, mock(PackageSessionProvider::class.java), mock(SilentUpdatePolicy::class.java), - mock(VerifierController::class.java) + mock(VerifierController::class.java), + mock(InstallDependencyHelper::class.java) ) ret.add(session) } catch (e: Exception) { diff --git a/services/tests/displayservicetests/AndroidManifest.xml b/services/tests/displayservicetests/AndroidManifest.xml index 37a34eeb9724..205ff058275a 100644 --- a/services/tests/displayservicetests/AndroidManifest.xml +++ b/services/tests/displayservicetests/AndroidManifest.xml @@ -29,7 +29,6 @@ <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" /> <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" /> <uses-permission android:name="android.permission.MANAGE_USB" /> - <uses-permission android:name="android.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE" /> <!-- Permissions needed for DisplayTransformManagerTest --> <uses-permission android:name="android.permission.CHANGE_CONFIGURATION" /> diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java index c741cae1c135..80e5ee39c13d 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java @@ -19,13 +19,16 @@ package com.android.server.display; import static android.Manifest.permission.ADD_ALWAYS_UNLOCKED_DISPLAY; import static android.Manifest.permission.ADD_TRUSTED_DISPLAY; import static android.Manifest.permission.CAPTURE_VIDEO_OUTPUT; +import static android.Manifest.permission.CONTROL_DISPLAY_BRIGHTNESS; import static android.Manifest.permission.MANAGE_DISPLAYS; +import static android.Manifest.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE; import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_ALWAYS_UNLOCKED; import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR; import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY; import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP; import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION; import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_TRUSTED; +import static android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS; import static android.view.ContentRecordingSession.RECORD_CONTENT_DISPLAY; import static android.view.ContentRecordingSession.RECORD_CONTENT_TASK; import static android.view.Display.HdrCapabilities.HDR_TYPE_INVALID; @@ -96,6 +99,7 @@ import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManagerGlobal; import android.hardware.display.DisplayManagerInternal; import android.hardware.display.DisplayManagerInternal.DisplayOffloader; +import android.hardware.display.DisplayTopology; import android.hardware.display.DisplayViewport; import android.hardware.display.DisplayedContentSample; import android.hardware.display.DisplayedContentSamplingAttributes; @@ -111,11 +115,13 @@ import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.MessageQueue; +import android.os.PermissionEnforcer; import android.os.PowerManager; import android.os.Process; import android.os.RemoteException; import android.os.SystemProperties; import android.os.UserManager; +import android.os.test.FakePermissionEnforcer; import android.platform.test.flag.junit.SetFlagsRule; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; @@ -251,6 +257,8 @@ public class DisplayManagerServiceTest { private int[] mAllowedHdrOutputTypes; + private final FakePermissionEnforcer mPermissionEnforcer = new FakePermissionEnforcer(); + private final DisplayManagerService.Injector mShortMockedInjector = new DisplayManagerService.Injector() { @Override @@ -428,6 +436,13 @@ public class DisplayManagerServiceTest { when(mContext.getResources()).thenReturn(mResources); mUserManager = Mockito.spy(mContext.getSystemService(UserManager.class)); + mPermissionEnforcer.grant(CONTROL_DISPLAY_BRIGHTNESS); + mPermissionEnforcer.grant(MODIFY_USER_PREFERRED_DISPLAY_MODE); + doReturn(Context.PERMISSION_ENFORCER_SERVICE).when(mContext).getSystemServiceName( + eq(PermissionEnforcer.class)); + doReturn(mPermissionEnforcer).when(mContext).getSystemService( + eq(Context.PERMISSION_ENFORCER_SERVICE)); + VirtualDeviceManager vdm = new VirtualDeviceManager(mIVirtualDeviceManager, mContext); when(mContext.getSystemService(VirtualDeviceManager.class)).thenReturn(vdm); when(mContext.getSystemService(UserManager.class)).thenReturn(mUserManager); @@ -3667,6 +3682,87 @@ public class DisplayManagerServiceTest { verify(mMockVirtualDisplayAdapter).releaseVirtualDisplayLocked(binder, callingUid); } + @Test + public void testGetDisplayTopology() { + Settings.Global.putInt(mContext.getContentResolver(), + DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS, 1); + manageDisplaysPermission(/* granted= */ true); + when(mMockFlags.isDisplayTopologyEnabled()).thenReturn(true); + DisplayManagerService displayManager = + new DisplayManagerService(mContext, mBasicInjector); + DisplayManagerInternal localService = displayManager.new LocalService(); + DisplayManagerService.BinderService displayManagerBinderService = + displayManager.new BinderService(); + registerDefaultDisplays(displayManager); + initDisplayPowerController(localService); + + DisplayTopology topology = displayManagerBinderService.getDisplayTopology(); + assertNotNull(topology); + DisplayTopology.TreeNode display = topology.getRoot(); + assertNotNull(display); + assertEquals(Display.DEFAULT_DISPLAY, display.getDisplayId()); + } + + @Test + public void testGetDisplayTopology_NullIfFlagDisabled() { + manageDisplaysPermission(/* granted= */ true); + when(mMockFlags.isDisplayTopologyEnabled()).thenReturn(false); + DisplayManagerService displayManager = + new DisplayManagerService(mContext, mBasicInjector); + DisplayManagerInternal localService = displayManager.new LocalService(); + DisplayManagerService.BinderService displayManagerBinderService = + displayManager.new BinderService(); + registerDefaultDisplays(displayManager); + initDisplayPowerController(localService); + + DisplayTopology topology = displayManagerBinderService.getDisplayTopology(); + assertNull(topology); + } + + @Test + public void testGetDisplayTopology_withoutPermission_shouldThrowException() { + when(mMockFlags.isDisplayTopologyEnabled()).thenReturn(true); + DisplayManagerService displayManager = + new DisplayManagerService(mContext, mBasicInjector); + DisplayManagerInternal localService = displayManager.new LocalService(); + DisplayManagerService.BinderService displayManagerBinderService = + displayManager.new BinderService(); + registerDefaultDisplays(displayManager); + initDisplayPowerController(localService); + + assertThrows(SecurityException.class, displayManagerBinderService::getDisplayTopology); + } + + @Test + public void testSetDisplayTopology() { + manageDisplaysPermission(/* granted= */ true); + when(mMockFlags.isDisplayTopologyEnabled()).thenReturn(true); + DisplayManagerService displayManager = + new DisplayManagerService(mContext, mBasicInjector); + DisplayManagerInternal localService = displayManager.new LocalService(); + DisplayManagerService.BinderService displayManagerBinderService = + displayManager.new BinderService(); + registerDefaultDisplays(displayManager); + initDisplayPowerController(localService); + + displayManagerBinderService.setDisplayTopology(new DisplayTopology()); + } + + @Test + public void testSetDisplayTopology_withoutPermission_shouldThrowException() { + when(mMockFlags.isDisplayTopologyEnabled()).thenReturn(true); + DisplayManagerService displayManager = + new DisplayManagerService(mContext, mBasicInjector); + DisplayManagerInternal localService = displayManager.new LocalService(); + DisplayManagerService.BinderService displayManagerBinderService = + displayManager.new BinderService(); + registerDefaultDisplays(displayManager); + initDisplayPowerController(localService); + + assertThrows(SecurityException.class, + () -> displayManagerBinderService.setDisplayTopology(new DisplayTopology())); + } + private void initDisplayPowerController(DisplayManagerInternal localService) { localService.initPowerManagement(new DisplayManagerInternal.DisplayPowerCallbacks() { @Override @@ -3850,6 +3946,10 @@ public class DisplayManagerServiceTest { DisplayDeviceInfo displayDeviceInfo = new DisplayDeviceInfo(); displayDeviceInfo.copyFrom(displayDevice.getDisplayDeviceInfoLocked()); displayDeviceInfo.modeId = modeId; + if (modeId > 0 && modeId <= displayDeviceInfo.supportedModes.length) { + displayDeviceInfo.renderFrameRate = + displayDeviceInfo.supportedModes[modeId - 1].getRefreshRate(); + } updateDisplayDeviceInfo(displayManager, displayDevice, displayDeviceInfo); } @@ -4017,9 +4117,11 @@ public class DisplayManagerServiceTest { private void manageDisplaysPermission(boolean granted) { if (granted) { doNothing().when(mContext).enforceCallingOrSelfPermission(eq(MANAGE_DISPLAYS), any()); + mPermissionEnforcer.grant(MANAGE_DISPLAYS); } else { doThrow(new SecurityException("MANAGE_DISPLAYS permission denied")).when(mContext) .enforceCallingOrSelfPermission(eq(MANAGE_DISPLAYS), any()); + mPermissionEnforcer.revoke(MANAGE_DISPLAYS); } } diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt b/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt index 85e73561cf59..a2d2a81b20b4 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt +++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt @@ -16,6 +16,7 @@ package com.android.server.display +import android.hardware.display.DisplayTopology import android.util.DisplayMetrics import android.view.Display import android.view.DisplayInfo diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyTest.kt b/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyTest.kt deleted file mode 100644 index cd8c26d0d337..000000000000 --- a/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyTest.kt +++ /dev/null @@ -1,476 +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.display - -import android.view.Display -import com.android.server.display.DisplayTopology.TreeNode.Position.POSITION_BOTTOM -import com.android.server.display.DisplayTopology.TreeNode.Position.POSITION_TOP -import com.android.server.display.DisplayTopology.TreeNode.Position.POSITION_RIGHT -import com.google.common.truth.Truth.assertThat -import org.junit.Test - -class DisplayTopologyTest { - private val topology = DisplayTopology() - - @Test - fun addOneDisplay() { - val displayId = 1 - val width = 800f - val height = 600f - - topology.addDisplay(displayId, width, height) - - assertThat(topology.mPrimaryDisplayId).isEqualTo(displayId) - - val display = topology.mRoot!! - assertThat(display.mDisplayId).isEqualTo(displayId) - assertThat(display.mWidth).isEqualTo(width) - assertThat(display.mHeight).isEqualTo(height) - assertThat(display.mChildren).isEmpty() - } - - @Test - fun addTwoDisplays() { - val displayId1 = 1 - val width1 = 800f - val height1 = 600f - - val displayId2 = 2 - val width2 = 1000f - val height2 = 1500f - - topology.addDisplay(displayId1, width1, height1) - topology.addDisplay(displayId2, width2, height2) - - assertThat(topology.mPrimaryDisplayId).isEqualTo(displayId1) - - val display1 = topology.mRoot!! - assertThat(display1.mDisplayId).isEqualTo(displayId1) - assertThat(display1.mWidth).isEqualTo(width1) - assertThat(display1.mHeight).isEqualTo(height1) - assertThat(display1.mChildren).hasSize(1) - - val display2 = display1.mChildren[0] - assertThat(display2.mDisplayId).isEqualTo(displayId2) - assertThat(display2.mWidth).isEqualTo(width2) - assertThat(display2.mHeight).isEqualTo(height2) - assertThat(display2.mChildren).isEmpty() - assertThat(display2.mPosition).isEqualTo(POSITION_TOP) - assertThat(display2.mOffset).isEqualTo(width1 / 2 - width2 / 2) - } - - @Test - fun addManyDisplays() { - val displayId1 = 1 - val width1 = 800f - val height1 = 600f - - val displayId2 = 2 - val width2 = 1000f - val height2 = 1500f - - topology.addDisplay(displayId1, width1, height1) - topology.addDisplay(displayId2, width2, height2) - - val noOfDisplays = 30 - for (i in 3..noOfDisplays) { - topology.addDisplay(/* displayId= */ i, width1, height1) - } - - assertThat(topology.mPrimaryDisplayId).isEqualTo(displayId1) - - val display1 = topology.mRoot!! - assertThat(display1.mDisplayId).isEqualTo(displayId1) - assertThat(display1.mWidth).isEqualTo(width1) - assertThat(display1.mHeight).isEqualTo(height1) - assertThat(display1.mChildren).hasSize(1) - - val display2 = display1.mChildren[0] - assertThat(display2.mDisplayId).isEqualTo(displayId2) - assertThat(display2.mWidth).isEqualTo(width2) - assertThat(display2.mHeight).isEqualTo(height2) - assertThat(display2.mChildren).hasSize(1) - assertThat(display2.mPosition).isEqualTo(POSITION_TOP) - assertThat(display2.mOffset).isEqualTo(width1 / 2 - width2 / 2) - - var display = display2 - for (i in 3..noOfDisplays) { - display = display.mChildren[0] - assertThat(display.mDisplayId).isEqualTo(i) - assertThat(display.mWidth).isEqualTo(width1) - assertThat(display.mHeight).isEqualTo(height1) - // The last display should have no children - assertThat(display.mChildren).hasSize(if (i < noOfDisplays) 1 else 0) - assertThat(display.mPosition).isEqualTo(POSITION_RIGHT) - assertThat(display.mOffset).isEqualTo(0) - } - } - - @Test - fun removeDisplays() { - val displayId1 = 1 - val width1 = 800f - val height1 = 600f - - val displayId2 = 2 - val width2 = 1000f - val height2 = 1500f - - topology.addDisplay(displayId1, width1, height1) - topology.addDisplay(displayId2, width2, height2) - - val noOfDisplays = 30 - for (i in 3..noOfDisplays) { - topology.addDisplay(/* displayId= */ i, width1, height1) - } - - var removedDisplays = arrayOf(20) - topology.removeDisplay(20) - - assertThat(topology.mPrimaryDisplayId).isEqualTo(displayId1) - - var display1 = topology.mRoot!! - assertThat(display1.mDisplayId).isEqualTo(displayId1) - assertThat(display1.mWidth).isEqualTo(width1) - assertThat(display1.mHeight).isEqualTo(height1) - assertThat(display1.mChildren).hasSize(1) - - var display2 = display1.mChildren[0] - assertThat(display2.mDisplayId).isEqualTo(displayId2) - assertThat(display2.mWidth).isEqualTo(width2) - assertThat(display2.mHeight).isEqualTo(height2) - assertThat(display2.mChildren).hasSize(1) - assertThat(display2.mPosition).isEqualTo(POSITION_TOP) - assertThat(display2.mOffset).isEqualTo(width1 / 2 - width2 / 2) - - var display = display2 - for (i in 3..noOfDisplays) { - if (i in removedDisplays) { - continue - } - display = display.mChildren[0] - assertThat(display.mDisplayId).isEqualTo(i) - assertThat(display.mWidth).isEqualTo(width1) - assertThat(display.mHeight).isEqualTo(height1) - // The last display should have no children - assertThat(display.mChildren).hasSize(if (i < noOfDisplays) 1 else 0) - assertThat(display.mPosition).isEqualTo(POSITION_RIGHT) - assertThat(display.mOffset).isEqualTo(0) - } - - topology.removeDisplay(22) - removedDisplays += 22 - topology.removeDisplay(23) - removedDisplays += 23 - topology.removeDisplay(25) - removedDisplays += 25 - - assertThat(topology.mPrimaryDisplayId).isEqualTo(displayId1) - - display1 = topology.mRoot!! - assertThat(display1.mDisplayId).isEqualTo(displayId1) - assertThat(display1.mWidth).isEqualTo(width1) - assertThat(display1.mHeight).isEqualTo(height1) - assertThat(display1.mChildren).hasSize(1) - - display2 = display1.mChildren[0] - assertThat(display2.mDisplayId).isEqualTo(displayId2) - assertThat(display2.mWidth).isEqualTo(width2) - assertThat(display2.mHeight).isEqualTo(height2) - assertThat(display2.mChildren).hasSize(1) - assertThat(display2.mPosition).isEqualTo(POSITION_TOP) - assertThat(display2.mOffset).isEqualTo(width1 / 2 - width2 / 2) - - display = display2 - for (i in 3..noOfDisplays) { - if (i in removedDisplays) { - continue - } - display = display.mChildren[0] - assertThat(display.mDisplayId).isEqualTo(i) - assertThat(display.mWidth).isEqualTo(width1) - assertThat(display.mHeight).isEqualTo(height1) - // The last display should have no children - assertThat(display.mChildren).hasSize(if (i < noOfDisplays) 1 else 0) - assertThat(display.mPosition).isEqualTo(POSITION_RIGHT) - assertThat(display.mOffset).isEqualTo(0) - } - } - - @Test - fun removeAllDisplays() { - val displayId = 1 - val width = 800f - val height = 600f - - topology.addDisplay(displayId, width, height) - topology.removeDisplay(displayId) - - assertThat(topology.mPrimaryDisplayId).isEqualTo(Display.INVALID_DISPLAY) - assertThat(topology.mRoot).isNull() - } - - @Test - fun removeDisplayThatDoesNotExist() { - val displayId = 1 - val width = 800f - val height = 600f - - topology.addDisplay(displayId, width, height) - topology.removeDisplay(3) - - assertThat(topology.mPrimaryDisplayId).isEqualTo(displayId) - - val display = topology.mRoot!! - assertThat(display.mDisplayId).isEqualTo(displayId) - assertThat(display.mWidth).isEqualTo(width) - assertThat(display.mHeight).isEqualTo(height) - assertThat(display.mChildren).isEmpty() - } - - @Test - fun removePrimaryDisplay() { - val displayId1 = 1 - val displayId2 = 2 - val width = 800f - val height = 600f - - topology.addDisplay(displayId1, width, height) - topology.addDisplay(displayId2, width, height) - topology.mPrimaryDisplayId = displayId2 - topology.removeDisplay(displayId2) - - assertThat(topology.mPrimaryDisplayId).isEqualTo(displayId1) - val display = topology.mRoot!! - assertThat(display.mDisplayId).isEqualTo(displayId1) - assertThat(display.mWidth).isEqualTo(width) - assertThat(display.mHeight).isEqualTo(height) - assertThat(display.mChildren).isEmpty() - } - - @Test - fun normalization_noOverlaps_leavesTopologyUnchanged() { - val display1 = DisplayTopology.TreeNode(/* displayId= */ 1, /* width= */ 200f, - /* height= */ 600f, /* position= */ null, /* offset= */ 0f) - topology.mRoot = display1 - - val display2 = DisplayTopology.TreeNode(/* displayId= */ 2, /* width= */ 600f, - /* height= */ 200f, POSITION_RIGHT, /* offset= */ 0f) - display1.mChildren.add(display2) - - val primaryDisplayId = 3 - val display3 = DisplayTopology.TreeNode(primaryDisplayId, /* width= */ 600f, - /* height= */ 200f, POSITION_RIGHT, /* offset= */ 400f) - display1.mChildren.add(display3) - topology.mPrimaryDisplayId = primaryDisplayId - - val display4 = DisplayTopology.TreeNode(/* displayId= */ 4, /* width= */ 200f, - /* height= */ 600f, POSITION_RIGHT, /* offset= */ 0f) - display2.mChildren.add(display4) - - topology.normalize() - - assertThat(topology.mPrimaryDisplayId).isEqualTo(primaryDisplayId) - - val actualDisplay1 = topology.mRoot!! - assertThat(actualDisplay1.mDisplayId).isEqualTo(1) - assertThat(actualDisplay1.mWidth).isEqualTo(200f) - assertThat(actualDisplay1.mHeight).isEqualTo(600f) - assertThat(actualDisplay1.mChildren).hasSize(2) - - val actualDisplay2 = actualDisplay1.mChildren[0] - assertThat(actualDisplay2.mDisplayId).isEqualTo(2) - assertThat(actualDisplay2.mWidth).isEqualTo(600f) - assertThat(actualDisplay2.mHeight).isEqualTo(200f) - assertThat(actualDisplay2.mPosition).isEqualTo(POSITION_RIGHT) - assertThat(actualDisplay2.mOffset).isEqualTo(0f) - assertThat(actualDisplay2.mChildren).hasSize(1) - - val actualDisplay3 = actualDisplay1.mChildren[1] - assertThat(actualDisplay3.mDisplayId).isEqualTo(3) - assertThat(actualDisplay3.mWidth).isEqualTo(600f) - assertThat(actualDisplay3.mHeight).isEqualTo(200f) - assertThat(actualDisplay3.mPosition).isEqualTo(POSITION_RIGHT) - assertThat(actualDisplay3.mOffset).isEqualTo(400f) - assertThat(actualDisplay3.mChildren).isEmpty() - - val actualDisplay4 = actualDisplay2.mChildren[0] - assertThat(actualDisplay4.mDisplayId).isEqualTo(4) - assertThat(actualDisplay4.mWidth).isEqualTo(200f) - assertThat(actualDisplay4.mHeight).isEqualTo(600f) - assertThat(actualDisplay4.mPosition).isEqualTo(POSITION_RIGHT) - assertThat(actualDisplay4.mOffset).isEqualTo(0f) - assertThat(actualDisplay4.mChildren).isEmpty() - } - - @Test - fun normalization_moveDisplayWithoutReparenting() { - val display1 = DisplayTopology.TreeNode(/* displayId= */ 1, /* width= */ 200f, - /* height= */ 600f, /* position= */ null, /* offset= */ 0f) - topology.mRoot = display1 - - val display2 = DisplayTopology.TreeNode(/* displayId= */ 2, /* width= */ 200f, - /* height= */ 600f, POSITION_RIGHT, /* offset= */ 0f) - display1.mChildren.add(display2) - - val primaryDisplayId = 3 - val display3 = DisplayTopology.TreeNode(primaryDisplayId, /* width= */ 600f, - /* height= */ 200f, POSITION_RIGHT, /* offset= */ 10f) - display1.mChildren.add(display3) - topology.mPrimaryDisplayId = primaryDisplayId - - val display4 = DisplayTopology.TreeNode(/* displayId= */ 4, /* width= */ 200f, - /* height= */ 600f, POSITION_RIGHT, /* offset= */ 0f) - display2.mChildren.add(display4) - - // Display 3 becomes a child of display 2. Display 4 gets moved without changing its parent. - topology.normalize() - - assertThat(topology.mPrimaryDisplayId).isEqualTo(primaryDisplayId) - - val actualDisplay1 = topology.mRoot!! - assertThat(actualDisplay1.mDisplayId).isEqualTo(1) - assertThat(actualDisplay1.mWidth).isEqualTo(200f) - assertThat(actualDisplay1.mHeight).isEqualTo(600f) - assertThat(actualDisplay1.mChildren).hasSize(1) - - val actualDisplay2 = actualDisplay1.mChildren[0] - assertThat(actualDisplay2.mDisplayId).isEqualTo(2) - assertThat(actualDisplay2.mWidth).isEqualTo(200f) - assertThat(actualDisplay2.mHeight).isEqualTo(600f) - assertThat(actualDisplay2.mPosition).isEqualTo(POSITION_RIGHT) - assertThat(actualDisplay2.mOffset).isEqualTo(0f) - assertThat(actualDisplay2.mChildren).hasSize(2) - - val actualDisplay3 = actualDisplay2.mChildren[1] - assertThat(actualDisplay3.mDisplayId).isEqualTo(3) - assertThat(actualDisplay3.mWidth).isEqualTo(600f) - assertThat(actualDisplay3.mHeight).isEqualTo(200f) - assertThat(actualDisplay3.mPosition).isEqualTo(POSITION_RIGHT) - assertThat(actualDisplay3.mOffset).isEqualTo(10f) - assertThat(actualDisplay3.mChildren).isEmpty() - - val actualDisplay4 = actualDisplay2.mChildren[0] - assertThat(actualDisplay4.mDisplayId).isEqualTo(4) - assertThat(actualDisplay4.mWidth).isEqualTo(200f) - assertThat(actualDisplay4.mHeight).isEqualTo(600f) - assertThat(actualDisplay4.mPosition).isEqualTo(POSITION_RIGHT) - assertThat(actualDisplay4.mOffset).isEqualTo(210f) - assertThat(actualDisplay4.mChildren).isEmpty() - } - - @Test - fun normalization_moveDisplayWithoutReparenting_offsetOutOfBounds() { - val display1 = DisplayTopology.TreeNode(/* displayId= */ 1, /* width= */ 200f, - /* height= */ 50f, /* position= */ null, /* offset= */ 0f) - topology.mRoot = display1 - - val display2 = DisplayTopology.TreeNode(/* displayId= */ 2, /* width= */ 600f, - /* height= */ 200f, POSITION_RIGHT, /* offset= */ 0f) - display1.mChildren.add(display2) - - val primaryDisplayId = 3 - val display3 = DisplayTopology.TreeNode(primaryDisplayId, /* width= */ 600f, - /* height= */ 200f, POSITION_RIGHT, /* offset= */ 10f) - display1.mChildren.add(display3) - topology.mPrimaryDisplayId = primaryDisplayId - - // Display 3 gets moved and its left side is still on the same line as the right side - // of Display 1, but it no longer touches it (the offset is out of bounds), so Display 2 - // becomes its new parent. - topology.normalize() - - assertThat(topology.mPrimaryDisplayId).isEqualTo(primaryDisplayId) - - val actualDisplay1 = topology.mRoot!! - assertThat(actualDisplay1.mDisplayId).isEqualTo(1) - assertThat(actualDisplay1.mWidth).isEqualTo(200f) - assertThat(actualDisplay1.mHeight).isEqualTo(50f) - assertThat(actualDisplay1.mChildren).hasSize(1) - - val actualDisplay2 = actualDisplay1.mChildren[0] - assertThat(actualDisplay2.mDisplayId).isEqualTo(2) - assertThat(actualDisplay2.mWidth).isEqualTo(600f) - assertThat(actualDisplay2.mHeight).isEqualTo(200f) - assertThat(actualDisplay2.mPosition).isEqualTo(POSITION_RIGHT) - assertThat(actualDisplay2.mOffset).isEqualTo(0f) - assertThat(actualDisplay2.mChildren).hasSize(1) - - val actualDisplay3 = actualDisplay2.mChildren[0] - assertThat(actualDisplay3.mDisplayId).isEqualTo(3) - assertThat(actualDisplay3.mWidth).isEqualTo(600f) - assertThat(actualDisplay3.mHeight).isEqualTo(200f) - assertThat(actualDisplay3.mPosition).isEqualTo(POSITION_BOTTOM) - assertThat(actualDisplay3.mOffset).isEqualTo(0f) - assertThat(actualDisplay3.mChildren).isEmpty() - } - - @Test - fun normalization_moveAndReparentDisplay() { - val display1 = DisplayTopology.TreeNode(/* displayId= */ 1, /* width= */ 200f, - /* height= */ 600f, /* position= */ null, /* offset= */ 0f) - topology.mRoot = display1 - - val display2 = DisplayTopology.TreeNode(/* displayId= */ 2, /* width= */ 200f, - /* height= */ 600f, POSITION_RIGHT, /* offset= */ 0f) - display1.mChildren.add(display2) - - val primaryDisplayId = 3 - val display3 = DisplayTopology.TreeNode(primaryDisplayId, /* width= */ 600f, - /* height= */ 200f, POSITION_RIGHT, /* offset= */ 400f) - display1.mChildren.add(display3) - topology.mPrimaryDisplayId = primaryDisplayId - - val display4 = DisplayTopology.TreeNode(/* displayId= */ 4, /* width= */ 200f, - /* height= */ 600f, POSITION_RIGHT, /* offset= */ 0f) - display2.mChildren.add(display4) - - topology.normalize() - - assertThat(topology.mPrimaryDisplayId).isEqualTo(primaryDisplayId) - - val actualDisplay1 = topology.mRoot!! - assertThat(actualDisplay1.mDisplayId).isEqualTo(1) - assertThat(actualDisplay1.mWidth).isEqualTo(200f) - assertThat(actualDisplay1.mHeight).isEqualTo(600f) - assertThat(actualDisplay1.mChildren).hasSize(1) - - val actualDisplay2 = actualDisplay1.mChildren[0] - assertThat(actualDisplay2.mDisplayId).isEqualTo(2) - assertThat(actualDisplay2.mWidth).isEqualTo(200f) - assertThat(actualDisplay2.mHeight).isEqualTo(600f) - assertThat(actualDisplay2.mPosition).isEqualTo(POSITION_RIGHT) - assertThat(actualDisplay2.mOffset).isEqualTo(0f) - assertThat(actualDisplay2.mChildren).hasSize(1) - - val actualDisplay3 = actualDisplay2.mChildren[0] - assertThat(actualDisplay3.mDisplayId).isEqualTo(3) - assertThat(actualDisplay3.mWidth).isEqualTo(600f) - assertThat(actualDisplay3.mHeight).isEqualTo(200f) - assertThat(actualDisplay3.mPosition).isEqualTo(POSITION_RIGHT) - assertThat(actualDisplay3.mOffset).isEqualTo(400f) - assertThat(actualDisplay3.mChildren).hasSize(1) - - val actualDisplay4 = actualDisplay3.mChildren[0] - assertThat(actualDisplay4.mDisplayId).isEqualTo(4) - assertThat(actualDisplay4.mWidth).isEqualTo(200f) - assertThat(actualDisplay4.mHeight).isEqualTo(600f) - assertThat(actualDisplay4.mPosition).isEqualTo(POSITION_RIGHT) - assertThat(actualDisplay4.mOffset).isEqualTo(-400f) - assertThat(actualDisplay4.mChildren).isEmpty() - } -}
\ No newline at end of file diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp index be698b2673ad..95acd75f32cc 100644 --- a/services/tests/mockingservicestests/Android.bp +++ b/services/tests/mockingservicestests/Android.bp @@ -109,6 +109,10 @@ android_test { optimize: { enabled: false, }, + + data: [ + ":HelloWorldUsingSdk1And2", + ], } java_library { @@ -125,22 +129,6 @@ java_library { ], } -android_ravenwood_test { - name: "FrameworksMockingServicesTestsRavenwood", - libs: [ - "android.test.mock.stubs.system", - ], - static_libs: [ - "androidx.annotation_annotation", - "androidx.test.rules", - "services.core", - ], - srcs: [ - "src/com/android/server/am/BroadcastRecordTest.java", - ], - auto_gen_config: true, -} - test_module_config { name: "FrameworksMockingServicesTests_blob", base: "FrameworksMockingServicesTests", diff --git a/services/tests/mockingservicestests/AndroidTest.xml b/services/tests/mockingservicestests/AndroidTest.xml index 7782d570856f..2b90119145bd 100644 --- a/services/tests/mockingservicestests/AndroidTest.xml +++ b/services/tests/mockingservicestests/AndroidTest.xml @@ -23,6 +23,12 @@ <option name="test-file-name" value="FrameworksMockingServicesTests.apk" /> </target_preparer> + <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"> + <option name="cleanup" value="true"/> + <option name="push-file" key="HelloWorldUsingSdk1And2.apk" + value="/data/local/tmp/tests/smockingservicestest/pm/HelloWorldUsingSdk1And2.apk"/> + </target_preparer> + <option name="test-tag" value="FrameworksMockingServicesTests" /> <test class="com.android.tradefed.testtype.AndroidJUnitTest" > 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 2a825f35bf62..dcbc23410fdb 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java @@ -1308,6 +1308,8 @@ public class ActivityManagerServiceTest { Intent intent = new Intent(); Intent extraIntent = new Intent("EXTRA_INTENT_ACTION"); intent.putExtra("EXTRA_INTENT0", extraIntent); + Intent nestedIntent = new Intent("NESTED_INTENT_ACTION"); + extraIntent.putExtra("NESTED_INTENT", nestedIntent); intent.collectExtraIntentKeys(); mAms.addCreatorToken(intent, TEST_PACKAGE); @@ -1317,6 +1319,11 @@ public class ActivityManagerServiceTest { assertThat(token).isNotNull(); assertThat(token.getCreatorUid()).isEqualTo(mInjector.getCallingUid()); assertThat(token.getCreatorPackage()).isEqualTo(TEST_PACKAGE); + + token = (ActivityManagerService.IntentCreatorToken) nestedIntent.getCreatorToken(); + assertThat(token).isNotNull(); + assertThat(token.getCreatorUid()).isEqualTo(mInjector.getCallingUid()); + assertThat(token.getCreatorPackage()).isEqualTo(TEST_PACKAGE); } @Test @@ -1349,6 +1356,8 @@ public class ActivityManagerServiceTest { Intent intent = new Intent(); Intent extraIntent = new Intent("EXTRA_INTENT_ACTION"); intent.putExtra("EXTRA_INTENT", extraIntent); + Intent nestedIntent = new Intent("NESTED_INTENT_ACTION"); + extraIntent.putExtra("NESTED_INTENT", nestedIntent); intent.collectExtraIntentKeys(); @@ -1374,9 +1383,12 @@ public class ActivityManagerServiceTest { extraIntent = intent.getParcelableExtra("EXTRA_INTENT", Intent.class); extraIntent2 = intent.getParcelableExtra("EXTRA_INTENT2", Intent.class); extraIntent3 = intent.getParcelableExtra("EXTRA_INTENT3", Intent.class); + nestedIntent = extraIntent.getParcelableExtra("NESTED_INTENT", Intent.class); assertThat(extraIntent.getExtendedFlags() & Intent.EXTENDED_FLAG_MISSING_CREATOR_OR_INVALID_TOKEN).isEqualTo(0); + assertThat(nestedIntent.getExtendedFlags() + & Intent.EXTENDED_FLAG_MISSING_CREATOR_OR_INVALID_TOKEN).isEqualTo(0); // sneaked in intent should have EXTENDED_FLAG_MISSING_CREATOR_OR_INVALID_TOKEN set. assertThat(extraIntent2.getExtendedFlags() & Intent.EXTENDED_FLAG_MISSING_CREATOR_OR_INVALID_TOKEN).isNotEqualTo(0); diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BaseBroadcastQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BaseBroadcastQueueTest.java index d602660597ff..a1a8b0ec7d2f 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/BaseBroadcastQueueTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/BaseBroadcastQueueTest.java @@ -41,6 +41,7 @@ import android.os.TestLooperManager; import android.os.UserHandle; import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; +import android.platform.test.flag.junit.SetFlagsRule; import android.provider.Settings; import android.util.SparseArray; @@ -97,6 +98,9 @@ public abstract class BaseBroadcastQueueTest { .spyStatic(ProcessList.class) .build(); + + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); @Rule public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); @@ -112,6 +116,8 @@ public abstract class BaseBroadcastQueueTest { AlarmManagerInternal mAlarmManagerInt; @Mock ProcessList mProcessList; + @Mock + PlatformCompat mPlatformCompat; @Mock AppStartInfoTracker mAppStartInfoTracker; @@ -178,6 +184,11 @@ public abstract class BaseBroadcastQueueTest { doReturn(false).when(mSkipPolicy).disallowBackgroundStart(any()); doReturn(mAppStartInfoTracker).when(mProcessList).getAppStartInfoTracker(); + + doReturn(true).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging( + eq(BroadcastFilter.CHANGE_RESTRICT_PRIORITY_VALUES), anyInt()); + doReturn(true).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging( + eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), anyInt()); } public void tearDown() throws Exception { diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java index 100b54897573..1481085c5f71 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java @@ -18,6 +18,7 @@ package com.android.server.am; import static android.app.ActivityManager.PROCESS_STATE_UNKNOWN; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static com.android.internal.util.FrameworkStatsLog.BROADCAST_DELIVERY_EVENT_REPORTED; import static com.android.internal.util.FrameworkStatsLog.BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_COLD; @@ -49,7 +50,6 @@ import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; @@ -65,7 +65,6 @@ import android.content.IIntentReceiver; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ApplicationInfo; -import android.content.pm.ResolveInfo; import android.media.AudioManager; import android.os.Bundle; import android.os.BundleMerger; @@ -73,6 +72,8 @@ import android.os.DropBoxManager; import android.os.Process; import android.os.SystemClock; import android.os.UserHandle; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags; import android.util.IndentingPrintWriter; import android.util.Pair; @@ -182,10 +183,6 @@ public final class BroadcastQueueModernImplTest extends BaseBroadcastQueueTest { return mock(Intent.class); } - private static ResolveInfo makeMockManifestReceiver() { - return mock(ResolveInfo.class); - } - private static BroadcastFilter makeMockRegisteredReceiver() { return mock(BroadcastFilter.class); } @@ -214,7 +211,8 @@ public final class BroadcastQueueModernImplTest extends BaseBroadcastQueueTest { return new BroadcastRecord(mImpl, intent, mProcess, PACKAGE_RED, null, 21, TEST_UID, false, null, null, null, null, AppOpsManager.OP_NONE, options, receivers, null, resultTo, Activity.RESULT_OK, null, null, ordered, false, false, UserHandle.USER_SYSTEM, - BackgroundStartPrivileges.NONE, false, null, PROCESS_STATE_UNKNOWN); + BackgroundStartPrivileges.NONE, false, null, PROCESS_STATE_UNKNOWN, + mPlatformCompat); } private void enqueueOrReplaceBroadcast(BroadcastProcessQueue queue, @@ -646,7 +644,8 @@ public final class BroadcastQueueModernImplTest extends BaseBroadcastQueueTest { @Test public void testRunnableAt_Cached_Manifest() { doRunnableAt_Cached(makeBroadcastRecord(makeMockIntent(), null, - List.of(makeMockManifestReceiver()), null, false), REASON_CONTAINS_MANIFEST); + List.of(makeManifestReceiver(PACKAGE_RED, CLASS_RED)), null, false), + REASON_CONTAINS_MANIFEST); } @Test @@ -679,6 +678,19 @@ public final class BroadcastQueueModernImplTest extends BaseBroadcastQueueTest { List.of(makeMockRegisteredReceiver()), null, false), REASON_CONTAINS_ALARM); } + @DisableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE) + @Test + public void testRunnableAt_Cached_Prioritized_NonDeferrable_flagDisabled() { + final List receivers = List.of( + withPriority(makeManifestReceiver(PACKAGE_RED, PACKAGE_RED), 10), + withPriority(makeManifestReceiver(PACKAGE_GREEN, PACKAGE_GREEN), -10)); + final BroadcastOptions options = BroadcastOptions.makeBasic() + .setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_NONE); + doRunnableAt_Cached(makeBroadcastRecord(makeMockIntent(), options, + receivers, null, false), REASON_CONTAINS_PRIORITIZED); + } + + @EnableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE) @Test public void testRunnableAt_Cached_Prioritized_NonDeferrable() { final List receivers = List.of( @@ -687,6 +699,32 @@ public final class BroadcastQueueModernImplTest extends BaseBroadcastQueueTest { final BroadcastOptions options = BroadcastOptions.makeBasic() .setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_NONE); doRunnableAt_Cached(makeBroadcastRecord(makeMockIntent(), options, + receivers, null, false), REASON_CONTAINS_MANIFEST); + } + + @Test + public void testRunnableAt_Cached_Ordered_NonDeferrable() { + final List receivers = List.of( + withPriority(makeManifestReceiver(PACKAGE_RED, PACKAGE_RED), 10), + withPriority(makeManifestReceiver(PACKAGE_GREEN, PACKAGE_GREEN), -10)); + final BroadcastOptions options = BroadcastOptions.makeBasic() + .setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_NONE); + doRunnableAt_Cached(makeBroadcastRecord(makeMockIntent(), options, + receivers, mock(IIntentReceiver.class), true), REASON_CONTAINS_ORDERED); + } + + @EnableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE) + @Test + public void testRunnableAt_Cached_Prioritized_NonDeferrable_changeIdDisabled() { + doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging( + eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), + eq(getUidForPackage(PACKAGE_GREEN))); + final List receivers = List.of( + withPriority(makeManifestReceiver(PACKAGE_RED, PACKAGE_RED), 10), + withPriority(makeManifestReceiver(PACKAGE_GREEN, PACKAGE_GREEN), -10)); + final BroadcastOptions options = BroadcastOptions.makeBasic() + .setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_NONE); + doRunnableAt_Cached(makeBroadcastRecord(makeMockIntent(), options, receivers, null, false), REASON_CONTAINS_PRIORITIZED); } @@ -1136,6 +1174,63 @@ public final class BroadcastQueueModernImplTest extends BaseBroadcastQueueTest { verifyPendingRecords(blueQueue, List.of(screenOn)); } + @DisableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE) + @SuppressWarnings("GuardedBy") + @Test + public void testDeliveryGroupPolicy_prioritized_diffReceivers_flagDisabled() { + final Intent screenOn = new Intent(Intent.ACTION_SCREEN_ON); + final Intent screenOff = new Intent(Intent.ACTION_SCREEN_OFF); + final BroadcastOptions screenOnOffOptions = BroadcastOptions.makeBasic() + .setDeliveryGroupPolicy(BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT) + .setDeliveryGroupMatchingKey("screenOnOff", Intent.ACTION_SCREEN_ON); + + final Object greenReceiver = withPriority( + makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN), 10); + final Object redReceiver = withPriority( + makeManifestReceiver(PACKAGE_RED, CLASS_RED), 5); + final Object blueReceiver = withPriority( + makeManifestReceiver(PACKAGE_BLUE, CLASS_BLUE), 0); + + mImpl.enqueueBroadcastLocked(makeBroadcastRecord(screenOn, screenOnOffOptions, + List.of(greenReceiver, blueReceiver), false)); + mImpl.enqueueBroadcastLocked(makeBroadcastRecord(screenOff, screenOnOffOptions, + List.of(greenReceiver, redReceiver, blueReceiver), false)); + final BroadcastProcessQueue greenQueue = mImpl.getProcessQueue(PACKAGE_GREEN, + getUidForPackage(PACKAGE_GREEN)); + final BroadcastProcessQueue redQueue = mImpl.getProcessQueue(PACKAGE_RED, + getUidForPackage(PACKAGE_RED)); + final BroadcastProcessQueue blueQueue = mImpl.getProcessQueue(PACKAGE_BLUE, + getUidForPackage(PACKAGE_BLUE)); + verifyPendingRecords(greenQueue, List.of(screenOff)); + verifyPendingRecords(redQueue, List.of(screenOff)); + verifyPendingRecords(blueQueue, List.of(screenOff)); + + assertTrue(greenQueue.isEmpty()); + assertTrue(redQueue.isEmpty()); + assertTrue(blueQueue.isEmpty()); + + mImpl.enqueueBroadcastLocked(makeBroadcastRecord(screenOff, screenOnOffOptions, + List.of(greenReceiver, redReceiver, blueReceiver), false)); + mImpl.enqueueBroadcastLocked(makeBroadcastRecord(screenOn, screenOnOffOptions, + List.of(greenReceiver, blueReceiver), false)); + verifyPendingRecords(greenQueue, List.of(screenOff, screenOn)); + verifyPendingRecords(redQueue, List.of(screenOff)); + verifyPendingRecords(blueQueue, List.of(screenOff, screenOn)); + + final BroadcastRecord screenOffRecord = makeBroadcastRecord(screenOff, screenOnOffOptions, + List.of(greenReceiver, redReceiver, blueReceiver), false); + screenOffRecord.setDeliveryState(2, BroadcastRecord.DELIVERY_DEFERRED, + "testDeliveryGroupPolicy_prioritized_diffReceivers_flagDisabled"); + mImpl.enqueueBroadcastLocked(screenOffRecord); + mImpl.enqueueBroadcastLocked(makeBroadcastRecord(screenOn, screenOnOffOptions, + List.of(greenReceiver, blueReceiver), false)); + verifyPendingRecords(greenQueue, List.of(screenOff, screenOn)); + verifyPendingRecords(redQueue, List.of(screenOff)); + verifyPendingRecords(blueQueue, List.of(screenOn)); + } + + @EnableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE) + @SuppressWarnings("GuardedBy") @Test public void testDeliveryGroupPolicy_prioritized_diffReceivers() { final Intent screenOn = new Intent(Intent.ACTION_SCREEN_ON); @@ -1173,6 +1268,65 @@ public final class BroadcastQueueModernImplTest extends BaseBroadcastQueueTest { List.of(greenReceiver, redReceiver, blueReceiver), false)); mImpl.enqueueBroadcastLocked(makeBroadcastRecord(screenOn, screenOnOffOptions, List.of(greenReceiver, blueReceiver), false)); + verifyPendingRecords(greenQueue, List.of(screenOn)); + verifyPendingRecords(redQueue, List.of(screenOff)); + verifyPendingRecords(blueQueue, List.of(screenOn)); + + final BroadcastRecord screenOffRecord = makeBroadcastRecord(screenOff, screenOnOffOptions, + List.of(greenReceiver, redReceiver, blueReceiver), false); + screenOffRecord.setDeliveryState(2, BroadcastRecord.DELIVERY_DEFERRED, + "testDeliveryGroupPolicy_prioritized_diffReceivers"); + mImpl.enqueueBroadcastLocked(screenOffRecord); + mImpl.enqueueBroadcastLocked(makeBroadcastRecord(screenOn, screenOnOffOptions, + List.of(greenReceiver, blueReceiver), false)); + verifyPendingRecords(greenQueue, List.of(screenOn)); + verifyPendingRecords(redQueue, List.of(screenOff)); + verifyPendingRecords(blueQueue, List.of(screenOn)); + } + + @EnableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE) + @SuppressWarnings("GuardedBy") + @Test + public void testDeliveryGroupPolicy_prioritized_diffReceivers_changeIdDisabled() { + doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging( + eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), + eq(getUidForPackage(PACKAGE_GREEN))); + + final Intent screenOn = new Intent(Intent.ACTION_SCREEN_ON); + final Intent screenOff = new Intent(Intent.ACTION_SCREEN_OFF); + final BroadcastOptions screenOnOffOptions = BroadcastOptions.makeBasic() + .setDeliveryGroupPolicy(BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT) + .setDeliveryGroupMatchingKey("screenOnOff", Intent.ACTION_SCREEN_ON); + + final Object greenReceiver = withPriority( + makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN), 10); + final Object redReceiver = withPriority( + makeManifestReceiver(PACKAGE_RED, CLASS_RED), 5); + final Object blueReceiver = withPriority( + makeManifestReceiver(PACKAGE_BLUE, CLASS_BLUE), 0); + + mImpl.enqueueBroadcastLocked(makeBroadcastRecord(screenOn, screenOnOffOptions, + List.of(greenReceiver, blueReceiver), false)); + mImpl.enqueueBroadcastLocked(makeBroadcastRecord(screenOff, screenOnOffOptions, + List.of(greenReceiver, redReceiver, blueReceiver), false)); + final BroadcastProcessQueue greenQueue = mImpl.getProcessQueue(PACKAGE_GREEN, + getUidForPackage(PACKAGE_GREEN)); + final BroadcastProcessQueue redQueue = mImpl.getProcessQueue(PACKAGE_RED, + getUidForPackage(PACKAGE_RED)); + final BroadcastProcessQueue blueQueue = mImpl.getProcessQueue(PACKAGE_BLUE, + getUidForPackage(PACKAGE_BLUE)); + verifyPendingRecords(greenQueue, List.of(screenOff)); + verifyPendingRecords(redQueue, List.of(screenOff)); + verifyPendingRecords(blueQueue, List.of(screenOff)); + + assertTrue(greenQueue.isEmpty()); + assertTrue(redQueue.isEmpty()); + assertTrue(blueQueue.isEmpty()); + + mImpl.enqueueBroadcastLocked(makeBroadcastRecord(screenOff, screenOnOffOptions, + List.of(greenReceiver, redReceiver, blueReceiver), false)); + mImpl.enqueueBroadcastLocked(makeBroadcastRecord(screenOn, screenOnOffOptions, + List.of(greenReceiver, blueReceiver), false)); verifyPendingRecords(greenQueue, List.of(screenOff, screenOn)); verifyPendingRecords(redQueue, List.of(screenOff)); verifyPendingRecords(blueQueue, List.of(screenOff, screenOn)); @@ -1569,8 +1723,9 @@ public final class BroadcastQueueModernImplTest extends BaseBroadcastQueueTest { verifyPendingRecords(redQueue, List.of(userPresent, timeTick)); } + @DisableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE) @Test - public void testDeliveryDeferredForCached() throws Exception { + public void testDeliveryDeferredForCached_flagDisabled() throws Exception { final ProcessRecord greenProcess = makeProcessRecord(makeApplicationInfo(PACKAGE_GREEN)); final ProcessRecord redProcess = makeProcessRecord(makeApplicationInfo(PACKAGE_RED)); @@ -1664,8 +1819,217 @@ public final class BroadcastQueueModernImplTest extends BaseBroadcastQueueTest { }, false /* andRemove */); } + @EnableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE) + @SuppressWarnings("GuardedBy") + @Test + public void testDeliveryDeferredForCached_changeIdDisabled() throws Exception { + doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging( + eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), + eq(getUidForPackage(PACKAGE_GREEN))); + + final ProcessRecord greenProcess = makeProcessRecord(makeApplicationInfo(PACKAGE_GREEN)); + final ProcessRecord redProcess = makeProcessRecord(makeApplicationInfo(PACKAGE_RED)); + + final Intent timeTick = new Intent(Intent.ACTION_TIME_TICK); + final BroadcastRecord timeTickRecord = makeBroadcastRecord(timeTick, + List.of(makeRegisteredReceiver(greenProcess, 0))); + + final Intent batteryChanged = new Intent(Intent.ACTION_BATTERY_CHANGED); + final BroadcastOptions optionsBatteryChanged = + BroadcastOptions.makeWithDeferUntilActive(true); + final BroadcastRecord batteryChangedRecord = makeBroadcastRecord(batteryChanged, + optionsBatteryChanged, + List.of(makeRegisteredReceiver(greenProcess, 10), + makeRegisteredReceiver(redProcess, 0)), + false /* ordered */); + + mImpl.enqueueBroadcastLocked(timeTickRecord); + mImpl.enqueueBroadcastLocked(batteryChangedRecord); + + final BroadcastProcessQueue greenQueue = mImpl.getProcessQueue(PACKAGE_GREEN, + getUidForPackage(PACKAGE_GREEN)); + final BroadcastProcessQueue redQueue = mImpl.getProcessQueue(PACKAGE_RED, + getUidForPackage(PACKAGE_RED)); + assertEquals(BroadcastProcessQueue.REASON_NORMAL, greenQueue.getRunnableAtReason()); + assertFalse(greenQueue.shouldBeDeferred()); + assertEquals(BroadcastProcessQueue.REASON_BLOCKED, redQueue.getRunnableAtReason()); + assertFalse(redQueue.shouldBeDeferred()); + + // Simulate process state change + greenQueue.setProcessAndUidState(greenProcess, false /* uidForeground */, + true /* processFreezable */); + greenQueue.updateDeferredStates(mImpl.mBroadcastConsumerDeferApply, + mImpl.mBroadcastConsumerDeferClear); + + assertEquals(BroadcastProcessQueue.REASON_CACHED, greenQueue.getRunnableAtReason()); + assertTrue(greenQueue.shouldBeDeferred()); + // Once the broadcasts to green process are deferred, broadcasts to red process + // shouldn't be blocked anymore. + assertEquals(BroadcastProcessQueue.REASON_NORMAL, redQueue.getRunnableAtReason()); + assertFalse(redQueue.shouldBeDeferred()); + + // All broadcasts to green process should be deferred. + greenQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> { + assertEquals("Unexpected state for " + r, + BroadcastRecord.DELIVERY_DEFERRED, r.getDeliveryState(i)); + }, false /* andRemove */); + redQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> { + assertEquals("Unexpected state for " + r, + BroadcastRecord.DELIVERY_PENDING, r.getDeliveryState(i)); + }, false /* andRemove */); + + final Intent packageChanged = new Intent(Intent.ACTION_PACKAGE_CHANGED); + final BroadcastRecord packageChangedRecord = makeBroadcastRecord(packageChanged, + List.of(makeRegisteredReceiver(greenProcess, 0))); + mImpl.enqueueBroadcastLocked(packageChangedRecord); + + assertEquals(BroadcastProcessQueue.REASON_CACHED, greenQueue.getRunnableAtReason()); + assertTrue(greenQueue.shouldBeDeferred()); + assertEquals(BroadcastProcessQueue.REASON_NORMAL, redQueue.getRunnableAtReason()); + assertFalse(redQueue.shouldBeDeferred()); + + // All broadcasts to the green process, including the newly enqueued one, should be + // deferred. + greenQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> { + assertEquals("Unexpected state for " + r, + BroadcastRecord.DELIVERY_DEFERRED, r.getDeliveryState(i)); + }, false /* andRemove */); + redQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> { + assertEquals("Unexpected state for " + r, + BroadcastRecord.DELIVERY_PENDING, r.getDeliveryState(i)); + }, false /* andRemove */); + + // Simulate process state change + greenQueue.setProcessAndUidState(greenProcess, false /* uidForeground */, + false /* processFreezable */); + greenQueue.updateDeferredStates(mImpl.mBroadcastConsumerDeferApply, + mImpl.mBroadcastConsumerDeferClear); + + assertEquals(BroadcastProcessQueue.REASON_NORMAL, greenQueue.getRunnableAtReason()); + assertFalse(greenQueue.shouldBeDeferred()); + assertEquals(BroadcastProcessQueue.REASON_NORMAL, redQueue.getRunnableAtReason()); + assertFalse(redQueue.shouldBeDeferred()); + + greenQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> { + assertEquals("Unexpected state for " + r, + BroadcastRecord.DELIVERY_PENDING, r.getDeliveryState(i)); + }, false /* andRemove */); + redQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> { + assertEquals("Unexpected state for " + r, + BroadcastRecord.DELIVERY_PENDING, r.getDeliveryState(i)); + }, false /* andRemove */); + } + + @DisableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE) + @SuppressWarnings("GuardedBy") + @Test + public void testDeliveryDeferredForCached_withInfiniteDeferred_flagDisabled() throws Exception { + final ProcessRecord greenProcess = makeProcessRecord(makeApplicationInfo(PACKAGE_GREEN)); + final ProcessRecord redProcess = makeProcessRecord(makeApplicationInfo(PACKAGE_RED)); + + final Intent timeTick = new Intent(Intent.ACTION_TIME_TICK); + final BroadcastOptions optionsTimeTick = BroadcastOptions.makeWithDeferUntilActive(true); + final BroadcastRecord timeTickRecord = makeBroadcastRecord(timeTick, optionsTimeTick, + List.of(makeRegisteredReceiver(greenProcess, 0)), false /* ordered */); + + final Intent batteryChanged = new Intent(Intent.ACTION_BATTERY_CHANGED); + final BroadcastOptions optionsBatteryChanged = + BroadcastOptions.makeWithDeferUntilActive(true); + final BroadcastRecord batteryChangedRecord = makeBroadcastRecord(batteryChanged, + optionsBatteryChanged, + List.of(makeRegisteredReceiver(greenProcess, 10), + makeRegisteredReceiver(redProcess, 0)), + false /* ordered */); + + mImpl.enqueueBroadcastLocked(timeTickRecord); + mImpl.enqueueBroadcastLocked(batteryChangedRecord); + + final BroadcastProcessQueue greenQueue = mImpl.getProcessQueue(PACKAGE_GREEN, + getUidForPackage(PACKAGE_GREEN)); + final BroadcastProcessQueue redQueue = mImpl.getProcessQueue(PACKAGE_RED, + getUidForPackage(PACKAGE_RED)); + assertEquals(BroadcastProcessQueue.REASON_NORMAL, greenQueue.getRunnableAtReason()); + assertFalse(greenQueue.shouldBeDeferred()); + assertEquals(BroadcastProcessQueue.REASON_BLOCKED, redQueue.getRunnableAtReason()); + assertFalse(redQueue.shouldBeDeferred()); + + // Simulate process state change + greenQueue.setProcessAndUidState(greenProcess, false /* uidForeground */, + true /* processFreezable */); + greenQueue.updateDeferredStates(mImpl.mBroadcastConsumerDeferApply, + mImpl.mBroadcastConsumerDeferClear); + + assertEquals(BroadcastProcessQueue.REASON_CACHED_INFINITE_DEFER, + greenQueue.getRunnableAtReason()); + assertTrue(greenQueue.shouldBeDeferred()); + // Once the broadcasts to green process are deferred, broadcasts to red process + // shouldn't be blocked anymore. + assertEquals(BroadcastProcessQueue.REASON_NORMAL, redQueue.getRunnableAtReason()); + assertFalse(redQueue.shouldBeDeferred()); + + // All broadcasts to green process should be deferred. + greenQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> { + assertEquals("Unexpected state for " + r, + BroadcastRecord.DELIVERY_DEFERRED, r.getDeliveryState(i)); + }, false /* andRemove */); + redQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> { + assertEquals("Unexpected state for " + r, + BroadcastRecord.DELIVERY_PENDING, r.getDeliveryState(i)); + }, false /* andRemove */); + + final Intent packageChanged = new Intent(Intent.ACTION_PACKAGE_CHANGED); + final BroadcastOptions optionsPackageChanged = + BroadcastOptions.makeWithDeferUntilActive(true); + final BroadcastRecord packageChangedRecord = makeBroadcastRecord(packageChanged, + optionsPackageChanged, + List.of(makeRegisteredReceiver(greenProcess, 0)), false /* ordered */); + mImpl.enqueueBroadcastLocked(packageChangedRecord); + + assertEquals(BroadcastProcessQueue.REASON_CACHED_INFINITE_DEFER, + greenQueue.getRunnableAtReason()); + assertTrue(greenQueue.shouldBeDeferred()); + assertEquals(BroadcastProcessQueue.REASON_NORMAL, redQueue.getRunnableAtReason()); + assertFalse(redQueue.shouldBeDeferred()); + + // All broadcasts to the green process, including the newly enqueued one, should be + // deferred. + greenQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> { + assertEquals("Unexpected state for " + r, + BroadcastRecord.DELIVERY_DEFERRED, r.getDeliveryState(i)); + }, false /* andRemove */); + redQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> { + assertEquals("Unexpected state for " + r, + BroadcastRecord.DELIVERY_PENDING, r.getDeliveryState(i)); + }, false /* andRemove */); + + // Simulate process state change + greenQueue.setProcessAndUidState(greenProcess, false /* uidForeground */, + false /* processFreezable */); + greenQueue.updateDeferredStates(mImpl.mBroadcastConsumerDeferApply, + mImpl.mBroadcastConsumerDeferClear); + + assertEquals(BroadcastProcessQueue.REASON_NORMAL, greenQueue.getRunnableAtReason()); + assertFalse(greenQueue.shouldBeDeferred()); + assertEquals(BroadcastProcessQueue.REASON_NORMAL, redQueue.getRunnableAtReason()); + assertFalse(redQueue.shouldBeDeferred()); + + greenQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> { + assertEquals("Unexpected state for " + r, + BroadcastRecord.DELIVERY_PENDING, r.getDeliveryState(i)); + }, false /* andRemove */); + redQueue.forEachMatchingBroadcast(BROADCAST_PREDICATE_ANY, (r, i) -> { + assertEquals("Unexpected state for " + r, + BroadcastRecord.DELIVERY_PENDING, r.getDeliveryState(i)); + }, false /* andRemove */); + } + + @EnableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE) @Test - public void testDeliveryDeferredForCached_withInfiniteDeferred() throws Exception { + public void testDeliveryDeferredForCached_withInfiniteDeferred_changeIdDisabled() + throws Exception { + doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging( + eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), + eq(getUidForPackage(PACKAGE_GREEN))); final ProcessRecord greenProcess = makeProcessRecord(makeApplicationInfo(PACKAGE_GREEN)); final ProcessRecord redProcess = makeProcessRecord(makeApplicationInfo(PACKAGE_RED)); diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java index 3aaf2e5c61a6..9d92d5fe4f60 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java @@ -21,6 +21,7 @@ import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_FINISH_RECEIVER import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_START_RECEIVER; import static android.os.UserHandle.USER_SYSTEM; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.server.am.ActivityManagerDebugConfig.LOG_WRITER_INFO; import static com.android.server.am.BroadcastProcessQueue.reasonToString; import static com.android.server.am.BroadcastRecord.deliveryStateToString; @@ -45,7 +46,6 @@ import static org.mockito.ArgumentMatchers.same; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -77,6 +77,8 @@ import android.os.IBinder; import android.os.PowerExemptionManager; import android.os.SystemClock; import android.os.UserHandle; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags; import android.platform.test.annotations.RequiresFlagsEnabled; import android.util.ArrayMap; import android.util.Log; @@ -446,7 +448,8 @@ public class BroadcastQueueTest extends BaseBroadcastQueueTest { callerApp.getPid(), callerApp.info.uid, false, null, null, null, null, AppOpsManager.OP_NONE, options, receivers, callerApp, resultTo, Activity.RESULT_OK, null, resultExtras, ordered, false, false, userId, - BackgroundStartPrivileges.NONE, false, null, PROCESS_STATE_UNKNOWN); + BackgroundStartPrivileges.NONE, false, null, PROCESS_STATE_UNKNOWN, + mPlatformCompat); } private void assertHealth() { @@ -1495,7 +1498,7 @@ public class BroadcastQueueTest extends BaseBroadcastQueueTest { null, null, null, null, AppOpsManager.OP_NONE, BroadcastOptions.makeBasic(), List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN)), null, null, Activity.RESULT_OK, null, null, false, false, false, UserHandle.USER_SYSTEM, - backgroundStartPrivileges, false, null, PROCESS_STATE_UNKNOWN); + backgroundStartPrivileges, false, null, PROCESS_STATE_UNKNOWN, mPlatformCompat); enqueueBroadcast(r); waitForIdle(); @@ -1550,8 +1553,10 @@ public class BroadcastQueueTest extends BaseBroadcastQueueTest { /** * Verify that when dispatching we respect tranches of priority. */ + @DisableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE) + @SuppressWarnings("DistinctVarargsChecker") @Test - public void testPriority() throws Exception { + public void testPriority_flagDisabled() throws Exception { final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED); final ProcessRecord receiverBlueApp = makeActiveProcessRecord(PACKAGE_BLUE); final ProcessRecord receiverGreenApp = makeActiveProcessRecord(PACKAGE_GREEN); @@ -1594,6 +1599,106 @@ public class BroadcastQueueTest extends BaseBroadcastQueueTest { } /** + * Verify that when dispatching we respect tranches of priority. + */ + @SuppressWarnings("DistinctVarargsChecker") + @Test + public void testOrdered_withPriorities() throws Exception { + final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED); + final ProcessRecord receiverBlueApp = makeActiveProcessRecord(PACKAGE_BLUE); + final ProcessRecord receiverGreenApp = makeActiveProcessRecord(PACKAGE_GREEN); + final ProcessRecord receiverYellowApp = makeActiveProcessRecord(PACKAGE_YELLOW); + + // Enqueue a normal broadcast that will go to several processes, and + // then enqueue a foreground broadcast that risks reordering + final Intent timezone = new Intent(Intent.ACTION_TIMEZONE_CHANGED); + final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED); + airplane.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); + final IIntentReceiver orderedResultTo = mock(IIntentReceiver.class); + enqueueBroadcast(makeOrderedBroadcastRecord(timezone, callerApp, + List.of(makeRegisteredReceiver(receiverBlueApp, 10), + makeRegisteredReceiver(receiverGreenApp, 10), + makeManifestReceiver(PACKAGE_BLUE, CLASS_BLUE), + makeManifestReceiver(PACKAGE_YELLOW, CLASS_YELLOW), + makeRegisteredReceiver(receiverYellowApp, -10)), + orderedResultTo, null)); + enqueueBroadcast(makeBroadcastRecord(airplane, callerApp, + List.of(makeRegisteredReceiver(receiverBlueApp)))); + waitForIdle(); + + // Ignore the final foreground broadcast + mScheduledBroadcasts.remove(makeScheduledBroadcast(receiverBlueApp, airplane)); + assertEquals(6, mScheduledBroadcasts.size()); + + // We're only concerned about enforcing ordering between tranches; + // within a tranche we're okay with reordering + assertEquals( + Set.of(makeScheduledBroadcast(receiverBlueApp, timezone), + makeScheduledBroadcast(receiverGreenApp, timezone)), + Set.of(mScheduledBroadcasts.remove(0), + mScheduledBroadcasts.remove(0))); + assertEquals( + Set.of(makeScheduledBroadcast(receiverBlueApp, timezone), + makeScheduledBroadcast(receiverYellowApp, timezone)), + Set.of(mScheduledBroadcasts.remove(0), + mScheduledBroadcasts.remove(0))); + assertEquals( + Set.of(makeScheduledBroadcast(receiverYellowApp, timezone)), + Set.of(mScheduledBroadcasts.remove(0))); + } + + /** + * Verify that when dispatching we respect tranches of priority. + */ + @EnableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE) + @SuppressWarnings("DistinctVarargsChecker") + @Test + public void testPriority_changeIdDisabled() throws Exception { + final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED); + final ProcessRecord receiverBlueApp = makeActiveProcessRecord(PACKAGE_BLUE); + final ProcessRecord receiverGreenApp = makeActiveProcessRecord(PACKAGE_GREEN); + final ProcessRecord receiverYellowApp = makeActiveProcessRecord(PACKAGE_YELLOW); + + doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging( + eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), eq(receiverBlueApp.uid)); + + // Enqueue a normal broadcast that will go to several processes, and + // then enqueue a foreground broadcast that risks reordering + final Intent timezone = new Intent(Intent.ACTION_TIMEZONE_CHANGED); + final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED); + airplane.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); + enqueueBroadcast(makeBroadcastRecord(timezone, callerApp, + List.of(makeRegisteredReceiver(receiverBlueApp, 10), + makeRegisteredReceiver(receiverGreenApp, 10), + makeManifestReceiver(PACKAGE_BLUE, CLASS_BLUE), + makeManifestReceiver(PACKAGE_YELLOW, CLASS_YELLOW), + makeRegisteredReceiver(receiverYellowApp, -10)))); + enqueueBroadcast(makeBroadcastRecord(airplane, callerApp, + List.of(makeRegisteredReceiver(receiverBlueApp)))); + waitForIdle(); + + // Ignore the final foreground broadcast + mScheduledBroadcasts.remove(makeScheduledBroadcast(receiverBlueApp, airplane)); + assertEquals(5, mScheduledBroadcasts.size()); + + // We're only concerned about enforcing ordering between tranches; + // within a tranche we're okay with reordering + assertEquals( + Set.of(makeScheduledBroadcast(receiverBlueApp, timezone), + makeScheduledBroadcast(receiverGreenApp, timezone)), + Set.of(mScheduledBroadcasts.remove(0), + mScheduledBroadcasts.remove(0))); + assertEquals( + Set.of(makeScheduledBroadcast(receiverBlueApp, timezone), + makeScheduledBroadcast(receiverYellowApp, timezone)), + Set.of(mScheduledBroadcasts.remove(0), + mScheduledBroadcasts.remove(0))); + assertEquals( + Set.of(makeScheduledBroadcast(receiverYellowApp, timezone)), + Set.of(mScheduledBroadcasts.remove(0))); + } + + /** * Verify prioritized receivers work as expected with deferrable broadcast - broadcast to * app in cached state should be deferred and the rest should be delivered as per the priority * order. @@ -2305,8 +2410,35 @@ public class BroadcastQueueTest extends BaseBroadcastQueueTest { .isLessThan(getReceiverScheduledTime(timeTickRecord, receiverBlue)); } + @DisableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE) + @Test + public void testPrioritizedBroadcastDelivery_uidForeground_flagDisabled() throws Exception { + final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED); + final ProcessRecord receiverBlueApp = makeActiveProcessRecord(PACKAGE_BLUE); + final ProcessRecord receiverGreenApp = makeActiveProcessRecord(PACKAGE_GREEN); + + mUidObserver.onUidStateChanged(receiverGreenApp.info.uid, + ActivityManager.PROCESS_STATE_TOP, 0, ActivityManager.PROCESS_CAPABILITY_NONE); + waitForIdle(); + + final Intent timeTick = new Intent(Intent.ACTION_TIME_TICK); + + final BroadcastFilter receiverBlue = makeRegisteredReceiver(receiverBlueApp, 10); + final BroadcastFilter receiverGreen = makeRegisteredReceiver(receiverGreenApp, 5); + final BroadcastRecord prioritizedRecord = makeBroadcastRecord(timeTick, callerApp, + List.of(receiverBlue, receiverGreen)); + + enqueueBroadcast(prioritizedRecord); + + waitForIdle(); + // Verify that uid foreground-ness does not impact that delivery of prioritized broadcast. + // That is, broadcast to receiverBlueApp gets scheduled before the one to receiverGreenApp. + assertThat(getReceiverScheduledTime(prioritizedRecord, receiverGreen)) + .isGreaterThan(getReceiverScheduledTime(prioritizedRecord, receiverBlue)); + } + @Test - public void testPrioritizedBroadcastDelivery_uidForeground() throws Exception { + public void testOrderedBroadcastDelivery_uidForeground() throws Exception { final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED); final ProcessRecord receiverBlueApp = makeActiveProcessRecord(PACKAGE_BLUE); final ProcessRecord receiverGreenApp = makeActiveProcessRecord(PACKAGE_GREEN); @@ -2319,6 +2451,37 @@ public class BroadcastQueueTest extends BaseBroadcastQueueTest { final BroadcastFilter receiverBlue = makeRegisteredReceiver(receiverBlueApp, 10); final BroadcastFilter receiverGreen = makeRegisteredReceiver(receiverGreenApp, 5); + final IIntentReceiver resultTo = mock(IIntentReceiver.class); + final BroadcastRecord prioritizedRecord = makeOrderedBroadcastRecord(timeTick, callerApp, + List.of(receiverBlue, receiverGreen), resultTo, null); + + enqueueBroadcast(prioritizedRecord); + + waitForIdle(); + // Verify that uid foreground-ness does not impact that delivery of prioritized broadcast. + // That is, broadcast to receiverBlueApp gets scheduled before the one to receiverGreenApp. + assertThat(getReceiverScheduledTime(prioritizedRecord, receiverGreen)) + .isGreaterThan(getReceiverScheduledTime(prioritizedRecord, receiverBlue)); + } + + @EnableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE) + @Test + public void testPrioritizedBroadcastDelivery_uidForeground_changeIdDisabled() throws Exception { + final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED); + final ProcessRecord receiverBlueApp = makeActiveProcessRecord(PACKAGE_BLUE); + final ProcessRecord receiverGreenApp = makeActiveProcessRecord(PACKAGE_GREEN); + + doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging( + eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), eq(receiverBlueApp.uid)); + + mUidObserver.onUidStateChanged(receiverGreenApp.info.uid, + ActivityManager.PROCESS_STATE_TOP, 0, ActivityManager.PROCESS_CAPABILITY_NONE); + waitForIdle(); + + final Intent timeTick = new Intent(Intent.ACTION_TIME_TICK); + + final BroadcastFilter receiverBlue = makeRegisteredReceiver(receiverBlueApp, 10); + final BroadcastFilter receiverGreen = makeRegisteredReceiver(receiverGreenApp, 5); final BroadcastRecord prioritizedRecord = makeBroadcastRecord(timeTick, callerApp, List.of(receiverBlue, receiverGreen)); diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastRecordTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastRecordTest.java index 8cd0da721364..a424bfdb8df4 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastRecordTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastRecordTest.java @@ -18,6 +18,8 @@ package com.android.server.am; import static android.app.ActivityManager.PROCESS_STATE_UNKNOWN; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static com.android.server.am.BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE; import static com.android.server.am.BroadcastRecord.DELIVERY_DEFERRED; import static com.android.server.am.BroadcastRecord.DELIVERY_DELIVERED; import static com.android.server.am.BroadcastRecord.DELIVERY_PENDING; @@ -33,6 +35,8 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; import android.app.BackgroundStartPrivileges; import android.app.BroadcastOptions; @@ -46,11 +50,17 @@ import android.os.Bundle; import android.os.PersistableBundle; import android.os.Process; 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.telephony.SubscriptionManager; import androidx.test.filters.SmallTest; +import com.android.server.compat.PlatformCompat; + import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; @@ -73,6 +83,9 @@ import java.util.function.BiFunction; public class BroadcastRecordTest { private static final String TAG = "BroadcastRecordTest"; + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + private static final int USER0 = UserHandle.USER_SYSTEM; private static final String PACKAGE1 = "pkg1"; private static final String PACKAGE2 = "pkg2"; @@ -89,10 +102,14 @@ public class BroadcastRecordTest { @Mock BroadcastQueue mQueue; @Mock ProcessRecord mProcess; + @Mock PlatformCompat mPlatformCompat; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); + + doReturn(true).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging( + eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), anyInt()); } @Test @@ -108,13 +125,13 @@ public class BroadcastRecordTest { assertArrayEquals(new int[] {-1}, calculateBlockedUntilBeyondCount(List.of( - createResolveInfo(PACKAGE1, getAppId(1), 0)), false)); + createResolveInfo(PACKAGE1, getAppId(1), 0)), false, mPlatformCompat)); assertArrayEquals(new int[] {-1}, calculateBlockedUntilBeyondCount(List.of( - createResolveInfo(PACKAGE1, getAppId(1), -10)), false)); + createResolveInfo(PACKAGE1, getAppId(1), -10)), false, mPlatformCompat)); assertArrayEquals(new int[] {-1}, calculateBlockedUntilBeyondCount(List.of( - createResolveInfo(PACKAGE1, getAppId(1), 10)), false)); + createResolveInfo(PACKAGE1, getAppId(1), 10)), false, mPlatformCompat)); } @Test @@ -128,18 +145,19 @@ public class BroadcastRecordTest { createResolveInfo(PACKAGE2, getAppId(2), 10), createResolveInfo(PACKAGE3, getAppId(3), 10)))); - assertArrayEquals(new int[] {-1,-1,-1}, + assertArrayEquals(new int[] {-1, -1, -1}, calculateBlockedUntilBeyondCount(List.of( createResolveInfo(PACKAGE1, getAppId(1), 0), createResolveInfo(PACKAGE2, getAppId(2), 0), - createResolveInfo(PACKAGE3, getAppId(3), 0)), false)); - assertArrayEquals(new int[] {-1,-1,-1}, + createResolveInfo(PACKAGE3, getAppId(3), 0)), false, mPlatformCompat)); + assertArrayEquals(new int[] {-1, -1, -1}, calculateBlockedUntilBeyondCount(List.of( createResolveInfo(PACKAGE1, getAppId(1), 10), createResolveInfo(PACKAGE2, getAppId(2), 10), - createResolveInfo(PACKAGE3, getAppId(3), 10)), false)); + createResolveInfo(PACKAGE3, getAppId(3), 10)), false, mPlatformCompat)); } + @DisableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE) @Test public void testIsPrioritized_Yes() { assertTrue(isPrioritized(List.of( @@ -151,18 +169,203 @@ public class BroadcastRecordTest { createResolveInfo(PACKAGE2, getAppId(2), 0), createResolveInfo(PACKAGE3, getAppId(3), 0)))); - assertArrayEquals(new int[] {0,1,2}, + assertArrayEquals(new int[] {0, 1, 2}, + calculateBlockedUntilBeyondCount(List.of( + createResolveInfo(PACKAGE1, getAppId(1), 10), + createResolveInfo(PACKAGE2, getAppId(2), 0), + createResolveInfo(PACKAGE3, getAppId(3), -10)), false, mPlatformCompat)); + assertArrayEquals(new int[] {0, 0, 2, 3, 3}, + calculateBlockedUntilBeyondCount(List.of( + createResolveInfo(PACKAGE1, getAppId(1), 20), + createResolveInfo(PACKAGE2, getAppId(2), 20), + createResolveInfo(PACKAGE3, getAppId(3), 10), + createResolveInfo(PACKAGE3, getAppId(3), 0), + createResolveInfo(PACKAGE3, getAppId(3), 0)), false, mPlatformCompat)); + } + + @EnableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE) + @Test + public void testIsPrioritized_withDifferentPriorities() { + assertFalse(isPrioritized(List.of( + createResolveInfo(PACKAGE1, getAppId(1), 10), + createResolveInfo(PACKAGE2, getAppId(2), 0), + createResolveInfo(PACKAGE3, getAppId(3), -10)))); + assertFalse(isPrioritized(List.of( + createResolveInfo(PACKAGE1, getAppId(1), 10), + createResolveInfo(PACKAGE2, getAppId(2), 0), + createResolveInfo(PACKAGE3, getAppId(3), 0)))); + + assertArrayEquals(new int[] {-1, -1, -1}, + calculateBlockedUntilBeyondCount(List.of( + createResolveInfo(PACKAGE1, getAppId(1), 10), + createResolveInfo(PACKAGE2, getAppId(2), 0), + createResolveInfo(PACKAGE3, getAppId(3), -10)), false, mPlatformCompat)); + assertArrayEquals(new int[] {-1, -1, -1}, + calculateBlockedUntilBeyondCount(List.of( + createResolveInfo(PACKAGE1, getAppId(1), 10), + createResolveInfo(PACKAGE2, getAppId(2), 10), + createResolveInfo(PACKAGE3, getAppId(3), -10)), false, mPlatformCompat)); + assertArrayEquals(new int[] {-1, -1, -1}, + calculateBlockedUntilBeyondCount(List.of( + createResolveInfo(PACKAGE1, getAppId(1), 10), + createResolveInfo(PACKAGE2, getAppId(2), 0), + createResolveInfo(PACKAGE3, getAppId(3), 0)), false, mPlatformCompat)); + assertArrayEquals(new int[] {-1, -1, -1, -1, -1}, + calculateBlockedUntilBeyondCount(List.of( + createResolveInfo(PACKAGE1, getAppId(1), 20), + createResolveInfo(PACKAGE2, getAppId(2), 20), + createResolveInfo(PACKAGE3, getAppId(3), 10), + createResolveInfo(PACKAGE3, getAppId(3), 0), + createResolveInfo(PACKAGE3, getAppId(3), 0)), false, mPlatformCompat)); + } + + @EnableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE) + @Test + public void testIsPrioritized_withDifferentPriorities_withFirstUidChangeIdDisabled() { + doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging( + eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), eq(getAppId(1))); + + assertTrue(isPrioritized(List.of( + createResolveInfo(PACKAGE1, getAppId(1), 10), + createResolveInfo(PACKAGE2, getAppId(2), 0), + createResolveInfo(PACKAGE3, getAppId(3), -10)))); + assertTrue(isPrioritized(List.of( + createResolveInfo(PACKAGE1, getAppId(1), 10), + createResolveInfo(PACKAGE2, getAppId(2), 0), + createResolveInfo(PACKAGE3, getAppId(3), 0)))); + + assertArrayEquals(new int[] {0, 1, 1}, calculateBlockedUntilBeyondCount(List.of( createResolveInfo(PACKAGE1, getAppId(1), 10), createResolveInfo(PACKAGE2, getAppId(2), 0), - createResolveInfo(PACKAGE3, getAppId(3), -10)), false)); - assertArrayEquals(new int[] {0,0,2,3,3}, + createResolveInfo(PACKAGE3, getAppId(3), -10)), false, mPlatformCompat)); + assertArrayEquals(new int[] {0, 0, 1}, + calculateBlockedUntilBeyondCount(List.of( + createResolveInfo(PACKAGE1, getAppId(1), 10), + createResolveInfo(PACKAGE2, getAppId(2), 10), + createResolveInfo(PACKAGE3, getAppId(3), -10)), false, mPlatformCompat)); + assertArrayEquals(new int[] {0, 0, 1, 1, 1}, calculateBlockedUntilBeyondCount(List.of( createResolveInfo(PACKAGE1, getAppId(1), 20), createResolveInfo(PACKAGE2, getAppId(2), 20), createResolveInfo(PACKAGE3, getAppId(3), 10), createResolveInfo(PACKAGE3, getAppId(3), 0), - createResolveInfo(PACKAGE3, getAppId(3), 0)), false)); + createResolveInfo(PACKAGE3, getAppId(3), 0)), false, mPlatformCompat)); + } + + @EnableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE) + @Test + public void testIsPrioritized_withDifferentPriorities_withLastUidChangeIdDisabled() { + doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging( + eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), eq(getAppId(3))); + + assertTrue(isPrioritized(List.of( + createResolveInfo(PACKAGE1, getAppId(1), 10), + createResolveInfo(PACKAGE2, getAppId(2), 0), + createResolveInfo(PACKAGE3, getAppId(3), -10)))); + assertTrue(isPrioritized(List.of( + createResolveInfo(PACKAGE1, getAppId(1), 10), + createResolveInfo(PACKAGE2, getAppId(2), 0), + createResolveInfo(PACKAGE3, getAppId(3), 0)))); + + assertArrayEquals(new int[] {0, 0, 2}, + calculateBlockedUntilBeyondCount(List.of( + createResolveInfo(PACKAGE1, getAppId(1), 10), + createResolveInfo(PACKAGE2, getAppId(2), 0), + createResolveInfo(PACKAGE3, getAppId(3), -10)), false, mPlatformCompat)); + assertArrayEquals(new int[] {0, 1}, + calculateBlockedUntilBeyondCount(List.of( + createResolveInfo(PACKAGE1, getAppId(1), 10), + createResolveInfo(PACKAGE3, getAppId(3), 0)), false, mPlatformCompat)); + assertArrayEquals(new int[] {0, 0, 1}, + calculateBlockedUntilBeyondCount(List.of( + createResolveInfo(PACKAGE1, getAppId(1), 10), + createResolveInfo(PACKAGE2, getAppId(2), 0), + createResolveInfo(PACKAGE3, getAppId(3), 0)), false, mPlatformCompat)); + assertArrayEquals(new int[] {0, 0, 2, 3, 3}, + calculateBlockedUntilBeyondCount(List.of( + createResolveInfo(PACKAGE1, getAppId(1), 20), + createResolveInfo(PACKAGE2, getAppId(2), 20), + createResolveInfo(PACKAGE3, getAppId(3), 10), + createResolveInfo(PACKAGE3, getAppId(3), 0), + createResolveInfo(PACKAGE3, getAppId(3), 0)), false, mPlatformCompat)); + } + + @EnableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE) + @Test + public void testIsPrioritized_withDifferentPriorities_withUidChangeIdDisabled() { + doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging( + eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), eq(getAppId(2))); + + assertTrue(isPrioritized(List.of( + createResolveInfo(PACKAGE1, getAppId(1), 10), + createResolveInfo(PACKAGE2, getAppId(2), 0), + createResolveInfo(PACKAGE3, getAppId(3), -10)))); + assertTrue(isPrioritized(List.of( + createResolveInfo(PACKAGE1, getAppId(1), 10), + createResolveInfo(PACKAGE2, getAppId(2), 0), + createResolveInfo(PACKAGE3, getAppId(3), 0)))); + + assertArrayEquals(new int[] {0, 1, 2}, + calculateBlockedUntilBeyondCount(List.of( + createResolveInfo(PACKAGE1, getAppId(1), 10), + createResolveInfo(PACKAGE2, getAppId(2), 0), + createResolveInfo(PACKAGE3, getAppId(3), -10)), false, mPlatformCompat)); + assertArrayEquals(new int[] {0, 1, 0}, + calculateBlockedUntilBeyondCount(List.of( + createResolveInfo(PACKAGE1, getAppId(1), 10), + createResolveInfo(PACKAGE2, getAppId(2), 0), + createResolveInfo(PACKAGE3, getAppId(3), 0)), false, mPlatformCompat)); + assertArrayEquals(new int[] {0, 0, 2, 2, 2}, + calculateBlockedUntilBeyondCount(List.of( + createResolveInfo(PACKAGE1, getAppId(1), 20), + createResolveInfo(PACKAGE2, getAppId(2), 20), + createResolveInfo(PACKAGE3, getAppId(3), 10), + createResolveInfo(PACKAGE3, getAppId(3), 0), + createResolveInfo(PACKAGE3, getAppId(4), 0)), false, mPlatformCompat)); + } + + @EnableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE) + @Test + public void testIsPrioritized_withDifferentPriorities_withMultipleUidChangeIdDisabled() { + doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging( + eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), eq(getAppId(1))); + doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging( + eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), eq(getAppId(2))); + + assertTrue(isPrioritized(List.of( + createResolveInfo(PACKAGE1, getAppId(1), 10), + createResolveInfo(PACKAGE2, getAppId(2), 0), + createResolveInfo(PACKAGE3, getAppId(3), -10)))); + assertTrue(isPrioritized(List.of( + createResolveInfo(PACKAGE1, getAppId(1), 10), + createResolveInfo(PACKAGE2, getAppId(2), 0), + createResolveInfo(PACKAGE3, getAppId(3), 0)))); + + assertArrayEquals(new int[] {0, 1, 2}, + calculateBlockedUntilBeyondCount(List.of( + createResolveInfo(PACKAGE1, getAppId(1), 10), + createResolveInfo(PACKAGE2, getAppId(2), 0), + createResolveInfo(PACKAGE3, getAppId(3), -10)), false, mPlatformCompat)); + assertArrayEquals(new int[] {0, 1, 1}, + calculateBlockedUntilBeyondCount(List.of( + createResolveInfo(PACKAGE1, getAppId(1), 10), + createResolveInfo(PACKAGE2, getAppId(2), 0), + createResolveInfo(PACKAGE3, getAppId(3), 0)), false, mPlatformCompat)); + assertArrayEquals(new int[] {0, 0, 2, 2, 2}, + calculateBlockedUntilBeyondCount(List.of( + createResolveInfo(PACKAGE1, getAppId(1), 20), + createResolveInfo(PACKAGE2, getAppId(2), 20), + createResolveInfo(PACKAGE3, getAppId(3), 10), + createResolveInfo(PACKAGE3, getAppId(3), 0), + createResolveInfo(PACKAGE3, getAppId(4), 0)), false, mPlatformCompat)); + assertArrayEquals(new int[] {0, 0, 1, 1, 3}, + calculateBlockedUntilBeyondCount(List.of( + createResolveInfo(PACKAGE1, getAppId(1), 20), + createResolveInfo(PACKAGE2, getAppId(3), 20), + createResolveInfo(PACKAGE3, getAppId(3), 10), + createResolveInfo(PACKAGE3, getAppId(3), 0), + createResolveInfo(PACKAGE3, getAppId(2), 0)), false, mPlatformCompat)); } @Test @@ -246,6 +449,77 @@ public class BroadcastRecordTest { assertTerminalDeferredBeyond(r, 3, 0, 3); } + @DisableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE) + @Test + public void testSetDeliveryState_DeferUntilActive_flagDisabled() { + final BroadcastRecord r = createBroadcastRecord( + new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED), List.of( + createResolveInfoWithPriority(10), + createResolveInfoWithPriority(10), + createResolveInfoWithPriority(10), + createResolveInfoWithPriority(0), + createResolveInfoWithPriority(0), + createResolveInfoWithPriority(0), + createResolveInfoWithPriority(-10), + createResolveInfoWithPriority(-10), + createResolveInfoWithPriority(-10))); + assertBlocked(r, false, false, false, true, true, true, true, true, true); + assertTerminalDeferredBeyond(r, 0, 0, 0); + + r.setDeliveryState(0, DELIVERY_PENDING, TAG); + r.setDeliveryState(1, DELIVERY_DEFERRED, TAG); + r.setDeliveryState(2, DELIVERY_PENDING, TAG); + r.setDeliveryState(3, DELIVERY_DEFERRED, TAG); + r.setDeliveryState(4, DELIVERY_DEFERRED, TAG); + r.setDeliveryState(5, DELIVERY_DEFERRED, TAG); + r.setDeliveryState(6, DELIVERY_DEFERRED, TAG); + r.setDeliveryState(7, DELIVERY_PENDING, TAG); + r.setDeliveryState(8, DELIVERY_DEFERRED, TAG); + + // Verify deferred counts ratchet up, but we're not "beyond" the first + // still-pending receiver + assertBlocked(r, false, false, false, true, true, true, true, true, true); + assertTerminalDeferredBeyond(r, 0, 6, 0); + + // We're still not "beyond" the first still-pending receiver, even when + // we finish a receiver later in the first tranche + r.setDeliveryState(2, DELIVERY_DELIVERED, TAG); + assertBlocked(r, false, false, false, true, true, true, true, true, true); + assertTerminalDeferredBeyond(r, 1, 6, 0); + + // Completing that last item in first tranche means we now unblock the + // second tranche, and since it's entirely deferred, the third traunche + // is unblocked too + r.setDeliveryState(0, DELIVERY_DELIVERED, TAG); + assertBlocked(r, false, false, false, false, false, false, false, false, false); + assertTerminalDeferredBeyond(r, 2, 6, 7); + + // Moving a deferred item in an earlier tranche back to being pending + // doesn't change the fact that we've already moved beyond it + r.setDeliveryState(1, DELIVERY_PENDING, TAG); + assertBlocked(r, false, false, false, false, false, false, false, false, false); + assertTerminalDeferredBeyond(r, 2, 5, 7); + r.setDeliveryState(1, DELIVERY_DELIVERED, TAG); + assertBlocked(r, false, false, false, false, false, false, false, false, false); + assertTerminalDeferredBeyond(r, 3, 5, 7); + + // Completing middle pending item is enough to fast-forward to end + r.setDeliveryState(7, DELIVERY_DELIVERED, TAG); + assertBlocked(r, false, false, false, false, false, false, false, false, false); + assertTerminalDeferredBeyond(r, 4, 5, 9); + + // Moving everyone else directly into a finished state updates all the + // terminal counters + r.setDeliveryState(3, DELIVERY_SKIPPED, TAG); + r.setDeliveryState(4, DELIVERY_SKIPPED, TAG); + r.setDeliveryState(5, DELIVERY_SKIPPED, TAG); + r.setDeliveryState(6, DELIVERY_SKIPPED, TAG); + r.setDeliveryState(8, DELIVERY_SKIPPED, TAG); + assertBlocked(r, false, false, false, false, false, false, false, false, false); + assertTerminalDeferredBeyond(r, 9, 0, 9); + } + + @EnableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE) @Test public void testSetDeliveryState_DeferUntilActive() { final BroadcastRecord r = createBroadcastRecord( @@ -259,6 +533,78 @@ public class BroadcastRecordTest { createResolveInfoWithPriority(-10), createResolveInfoWithPriority(-10), createResolveInfoWithPriority(-10))); + assertBlocked(r, false, false, false, false, false, false, false, false, false); + assertTerminalDeferredBeyond(r, 0, 0, 0); + + r.setDeliveryState(0, DELIVERY_PENDING, TAG); + r.setDeliveryState(1, DELIVERY_DEFERRED, TAG); + r.setDeliveryState(2, DELIVERY_PENDING, TAG); + r.setDeliveryState(3, DELIVERY_DEFERRED, TAG); + r.setDeliveryState(4, DELIVERY_DEFERRED, TAG); + r.setDeliveryState(5, DELIVERY_DEFERRED, TAG); + r.setDeliveryState(6, DELIVERY_DEFERRED, TAG); + r.setDeliveryState(7, DELIVERY_PENDING, TAG); + r.setDeliveryState(8, DELIVERY_DEFERRED, TAG); + + // Verify deferred counts ratchet up, but we're not "beyond" the first + // still-pending receiver + assertBlocked(r, false, false, false, false, false, false, false, false, false); + assertTerminalDeferredBeyond(r, 0, 6, 0); + + // We're still not "beyond" the first still-pending receiver, even when + // we finish a receiver later in the first tranche + r.setDeliveryState(2, DELIVERY_DELIVERED, TAG); + assertBlocked(r, false, false, false, false, false, false, false, false, false); + assertTerminalDeferredBeyond(r, 1, 6, 0); + + // Completing that last item in first tranche means we now unblock the + // second tranche, and since it's entirely deferred, the third traunche + // is unblocked too + r.setDeliveryState(0, DELIVERY_DELIVERED, TAG); + assertBlocked(r, false, false, false, false, false, false, false, false, false); + assertTerminalDeferredBeyond(r, 2, 6, 7); + + // Moving a deferred item in an earlier tranche back to being pending + // doesn't change the fact that we've already moved beyond it + r.setDeliveryState(1, DELIVERY_PENDING, TAG); + assertBlocked(r, false, false, false, false, false, false, false, false, false); + assertTerminalDeferredBeyond(r, 2, 5, 7); + r.setDeliveryState(1, DELIVERY_DELIVERED, TAG); + assertBlocked(r, false, false, false, false, false, false, false, false, false); + assertTerminalDeferredBeyond(r, 3, 5, 7); + + // Completing middle pending item is enough to fast-forward to end + r.setDeliveryState(7, DELIVERY_DELIVERED, TAG); + assertBlocked(r, false, false, false, false, false, false, false, false, false); + assertTerminalDeferredBeyond(r, 4, 5, 9); + + // Moving everyone else directly into a finished state updates all the + // terminal counters + r.setDeliveryState(3, DELIVERY_SKIPPED, TAG); + r.setDeliveryState(4, DELIVERY_SKIPPED, TAG); + r.setDeliveryState(5, DELIVERY_SKIPPED, TAG); + r.setDeliveryState(6, DELIVERY_SKIPPED, TAG); + r.setDeliveryState(8, DELIVERY_SKIPPED, TAG); + assertBlocked(r, false, false, false, false, false, false, false, false, false); + assertTerminalDeferredBeyond(r, 9, 0, 9); + } + + @EnableFlags(Flags.FLAG_LIMIT_PRIORITY_SCOPE) + @Test + public void testSetDeliveryState_DeferUntilActive_changeIdDisabled() { + doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging( + eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), eq(getAppId(1))); + final BroadcastRecord r = createBroadcastRecord( + new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED), List.of( + createResolveInfo(PACKAGE1, getAppId(1), 10), + createResolveInfo(PACKAGE1, getAppId(1), 10), + createResolveInfo(PACKAGE1, getAppId(1), 10), + createResolveInfo(PACKAGE1, getAppId(1), 0), + createResolveInfo(PACKAGE1, getAppId(1), 0), + createResolveInfo(PACKAGE1, getAppId(1), 0), + createResolveInfo(PACKAGE1, getAppId(1), -10), + createResolveInfo(PACKAGE1, getAppId(1), -10), + createResolveInfo(PACKAGE1, getAppId(1), -10))); assertBlocked(r, false, false, false, true, true, true, true, true, true); assertTerminalDeferredBeyond(r, 0, 0, 0); @@ -602,6 +948,66 @@ public class BroadcastRecordTest { assertTrue(record3.matchesDeliveryGroup(record1)); } + @Test + public void testCalculateChangeStateForReceivers() { + assertArrayEquals(new boolean[] {true, true, true}, calculateChangeState( + List.of(createResolveInfo(PACKAGE1, getAppId(1)), + createResolveInfo(PACKAGE2, getAppId(2)), + createResolveInfo(PACKAGE3, getAppId(3))))); + assertArrayEquals(new boolean[] {true, true, true, true}, calculateChangeState( + List.of(createResolveInfo(PACKAGE1, getAppId(1)), + createResolveInfo(PACKAGE2, getAppId(2)), + createResolveInfo(PACKAGE2, getAppId(2)), + createResolveInfo(PACKAGE3, getAppId(3))))); + + doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging( + eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), eq(getAppId(1))); + assertArrayEquals(new boolean[] {false, true, true}, calculateChangeState( + List.of(createResolveInfo(PACKAGE1, getAppId(1)), + createResolveInfo(PACKAGE2, getAppId(2)), + createResolveInfo(PACKAGE3, getAppId(3))))); + assertArrayEquals(new boolean[] {false, true, false, true}, calculateChangeState( + List.of(createResolveInfo(PACKAGE1, getAppId(1)), + createResolveInfo(PACKAGE2, getAppId(2)), + createResolveInfo(PACKAGE2, getAppId(1)), + createResolveInfo(PACKAGE3, getAppId(3))))); + + doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging( + eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), eq(getAppId(2))); + assertArrayEquals(new boolean[] {false, false, true}, calculateChangeState( + List.of(createResolveInfo(PACKAGE1, getAppId(1)), + createResolveInfo(PACKAGE2, getAppId(2)), + createResolveInfo(PACKAGE3, getAppId(3))))); + assertArrayEquals(new boolean[] {false, true, false, false, false, true}, + calculateChangeState( + List.of(createResolveInfo(PACKAGE1, getAppId(1)), + createResolveInfo(PACKAGE3, getAppId(3)), + createResolveInfo(PACKAGE2, getAppId(2)), + createResolveInfo(PACKAGE2, getAppId(1)), + createResolveInfo(PACKAGE2, getAppId(2)), + createResolveInfo(PACKAGE3, getAppId(3))))); + + doReturn(false).when(mPlatformCompat).isChangeEnabledByUidInternalNoLogging( + eq(BroadcastRecord.CHANGE_LIMIT_PRIORITY_SCOPE), eq(getAppId(3))); + assertArrayEquals(new boolean[] {false, false, false}, calculateChangeState( + List.of(createResolveInfo(PACKAGE1, getAppId(1)), + createResolveInfo(PACKAGE2, getAppId(2)), + createResolveInfo(PACKAGE3, getAppId(3))))); + assertArrayEquals(new boolean[] {false, false, false, false, false, false}, + calculateChangeState( + List.of(createResolveInfo(PACKAGE1, getAppId(1)), + createResolveInfo(PACKAGE3, getAppId(3)), + createResolveInfo(PACKAGE2, getAppId(2)), + createResolveInfo(PACKAGE2, getAppId(1)), + createResolveInfo(PACKAGE2, getAppId(2)), + createResolveInfo(PACKAGE3, getAppId(3))))); + } + + private boolean[] calculateChangeState(List<Object> receivers) { + return BroadcastRecord.calculateChangeStateForReceivers(receivers, + CHANGE_LIMIT_PRIORITY_SCOPE, mPlatformCompat); + } + private static void cleanupDisabledPackageReceivers(BroadcastRecord record, String packageName, int userId) { record.cleanupDisabledPackageReceiversLocked(packageName, null /* filterByClasses */, @@ -753,16 +1159,17 @@ public class BroadcastRecordTest { BackgroundStartPrivileges.NONE, false /* timeoutExempt */, filterExtrasForReceiver, - PROCESS_STATE_UNKNOWN); + PROCESS_STATE_UNKNOWN, + mPlatformCompat); } private static int getAppId(int i) { return Process.FIRST_APPLICATION_UID + i; } - private static boolean isPrioritized(List<Object> receivers) { + private boolean isPrioritized(List<Object> receivers) { return BroadcastRecord.isPrioritized( - calculateBlockedUntilBeyondCount(receivers, false), false); + calculateBlockedUntilBeyondCount(receivers, false, mPlatformCompat), false); } private static void assertBlocked(BroadcastRecord r, boolean... blocked) { diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java index 4a1315583ad4..f82a86092064 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java @@ -109,6 +109,7 @@ import android.platform.test.annotations.Presubmit; import android.platform.test.flag.junit.SetFlagsRule; import android.util.ArrayMap; import android.util.SparseArray; +import android.util.SparseBooleanArray; import android.util.SparseIntArray; import com.android.server.LocalServices; @@ -175,6 +176,7 @@ public class MockingOomAdjusterTests { private ActiveUids mActiveUids; private PackageManagerInternal mPackageManagerInternal; private ActivityManagerService mService; + private TestCachedAppOptimizer mTestCachedAppOptimizer; private OomAdjusterInjector mInjector = new OomAdjusterInjector(); private int mUiTierSize; @@ -242,9 +244,11 @@ public class MockingOomAdjusterTests { doNothing().when(pr).enqueueProcessChangeItemLocked(anyInt(), anyInt(), anyInt(), anyBoolean()); mActiveUids = new ActiveUids(mService, false); + mTestCachedAppOptimizer = new TestCachedAppOptimizer(mService); mProcessStateController = new ProcessStateController.Builder(mService, mService.mProcessList, mActiveUids) .useModernOomAdjuster(mService.mConstants.ENABLE_NEW_OOMADJ) + .setCachedAppOptimizer(mTestCachedAppOptimizer) .setOomAdjusterInjector(mInjector) .build(); mService.mProcessStateController = mProcessStateController; @@ -3110,13 +3114,13 @@ public class MockingOomAdjusterTests { mProcessStateController.setUidTempAllowlistStateLSP(MOCKAPP_UID, true); assertEquals(true, app.getUidRecord().isSetAllowListed()); - assertEquals(true, app.mOptRecord.shouldNotFreeze()); - assertEquals(true, app2.mOptRecord.shouldNotFreeze()); + assertFreezeState(app, false); + assertFreezeState(app2, false); mProcessStateController.setUidTempAllowlistStateLSP(MOCKAPP_UID, false); assertEquals(false, app.getUidRecord().isSetAllowListed()); - assertEquals(false, app.mOptRecord.shouldNotFreeze()); - assertEquals(false, app2.mOptRecord.shouldNotFreeze()); + assertFreezeState(app, true); + assertFreezeState(app2, true); } @SuppressWarnings("GuardedBy") @@ -3138,25 +3142,25 @@ public class MockingOomAdjusterTests { assertEquals(true, app.getUidRecord().isSetAllowListed()); assertEquals(true, app2.getUidRecord().isSetAllowListed()); - assertEquals(true, app.mOptRecord.shouldNotFreeze()); - assertEquals(true, app2.mOptRecord.shouldNotFreeze()); - assertEquals(true, app3.mOptRecord.shouldNotFreeze()); + assertFreezeState(app, false); + assertFreezeState(app2, false); + assertFreezeState(app3, false); // Remove app1 from allowlist. mProcessStateController.setUidTempAllowlistStateLSP(MOCKAPP_UID, false); assertEquals(false, app.getUidRecord().isSetAllowListed()); assertEquals(true, app2.getUidRecord().isSetAllowListed()); - assertEquals(false, app.mOptRecord.shouldNotFreeze()); - assertEquals(true, app2.mOptRecord.shouldNotFreeze()); - assertEquals(true, app3.mOptRecord.shouldNotFreeze()); + assertFreezeState(app, true); + assertFreezeState(app2, false); + assertFreezeState(app3, false); // Now remove app2 from allowlist. mProcessStateController.setUidTempAllowlistStateLSP(MOCKAPP2_UID, false); assertEquals(false, app.getUidRecord().isSetAllowListed()); assertEquals(false, app2.getUidRecord().isSetAllowListed()); - assertEquals(false, app.mOptRecord.shouldNotFreeze()); - assertEquals(false, app2.mOptRecord.shouldNotFreeze()); - assertEquals(false, app3.mOptRecord.shouldNotFreeze()); + assertFreezeState(app, true); + assertFreezeState(app2, true); + assertFreezeState(app3, true); } @SuppressWarnings("GuardedBy") @@ -3370,6 +3374,14 @@ public class MockingOomAdjusterTests { assertEquals(expectedCached, state.isCached()); } + @SuppressWarnings("GuardedBy") + private void assertFreezeState(ProcessRecord app, boolean expectedFreezeState) { + boolean actualFreezeState = mTestCachedAppOptimizer.mLastSetFreezeState.get(app.getPid(), + false); + assertEquals("Unexcepted freeze state for " + app.processName, expectedFreezeState, + actualFreezeState); + } + private class ProcessRecordBuilder { @SuppressWarnings("UnusedVariable") int mPid; @@ -3513,6 +3525,39 @@ public class MockingOomAdjusterTests { return app; } } + private static final class TestProcessDependencies + implements CachedAppOptimizer.ProcessDependencies { + @Override + public long[] getRss(int pid) { + return new long[]{/*totalRSS*/ 0, /*fileRSS*/ 0, /*anonRSS*/ 0, /*swap*/ 0}; + } + + @Override + public void performCompaction(CachedAppOptimizer.CompactProfile action, int pid) {} + } + + private static class TestCachedAppOptimizer extends CachedAppOptimizer { + private SparseBooleanArray mLastSetFreezeState = new SparseBooleanArray(); + + TestCachedAppOptimizer(ActivityManagerService ams) { + super(ams, null, new TestProcessDependencies()); + } + + @Override + public boolean useFreezer() { + return true; + } + + @Override + public void freezeAppAsyncLSP(ProcessRecord app) { + mLastSetFreezeState.put(app.getPid(), true); + } + + @Override + public void unfreezeAppLSP(ProcessRecord app, @UnfreezeReason int reason) { + mLastSetFreezeState.put(app.getPid(), false); + } + } static class OomAdjusterInjector extends OomAdjuster.Injector { // Jump ahead in time by this offset amount. @@ -3524,7 +3569,6 @@ public class MockingOomAdjusterTests { mLastSetOomAdj.clear(); } - void jumpUptimeAheadTo(long uptimeMillis) { final long jumpMs = uptimeMillis - getUptimeMillis(); if (jumpMs <= 0) return; diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/InstallDependencyHelperTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/InstallDependencyHelperTest.java new file mode 100644 index 000000000000..f6c644e3d4d4 --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/pm/InstallDependencyHelperTest.java @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.pm; + +import static android.content.pm.Flags.FLAG_SDK_DEPENDENCY_INSTALLER; + +import static com.google.common.truth.Truth.assertThat; + +import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import android.content.pm.SharedLibraryInfo; +import android.content.pm.parsing.ApkLite; +import android.content.pm.parsing.ApkLiteParseUtils; +import android.content.pm.parsing.PackageLite; +import android.content.pm.parsing.result.ParseResult; +import android.content.pm.parsing.result.ParseTypeImpl; +import android.os.FileUtils; +import android.os.OutcomeReceiver; +import android.platform.test.annotations.Presubmit; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; + +import androidx.annotation.NonNull; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +@Presubmit +@RunWith(JUnit4.class) +@RequiresFlagsEnabled(FLAG_SDK_DEPENDENCY_INSTALLER) +public class InstallDependencyHelperTest { + + @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder(); + @Rule public final CheckFlagsRule checkFlagsRule = + DeviceFlagsValueProvider.createCheckFlagsRule(); + + private static final String PUSH_FILE_DIR = "/data/local/tmp/tests/smockingservicestest/pm/"; + private static final String TEST_APP_USING_SDK1_AND_SDK2 = "HelloWorldUsingSdk1And2.apk"; + + @Mock private SharedLibrariesImpl mSharedLibraries; + private InstallDependencyHelper mInstallDependencyHelper; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mInstallDependencyHelper = new InstallDependencyHelper(mSharedLibraries); + } + + @Test + public void testResolveLibraryDependenciesIfNeeded_errorInSharedLibrariesImpl() + throws Exception { + doThrow(new PackageManagerException(new Exception("xyz"))) + .when(mSharedLibraries).collectMissingSharedLibraryInfos(any()); + + PackageLite pkg = getPackageLite(TEST_APP_USING_SDK1_AND_SDK2); + CallbackHelper callback = new CallbackHelper(/*expectSuccess=*/ false); + mInstallDependencyHelper.resolveLibraryDependenciesIfNeeded(pkg, callback); + callback.assertFailure(); + + assertThat(callback.error).hasMessageThat().contains("xyz"); + } + + @Test + public void testResolveLibraryDependenciesIfNeeded_failsToBind() throws Exception { + // Return a non-empty list as missing dependency + PackageLite pkg = getPackageLite(TEST_APP_USING_SDK1_AND_SDK2); + List<SharedLibraryInfo> missingDependency = Collections.singletonList( + mock(SharedLibraryInfo.class)); + when(mSharedLibraries.collectMissingSharedLibraryInfos(eq(pkg))) + .thenReturn(missingDependency); + + CallbackHelper callback = new CallbackHelper(/*expectSuccess=*/ false); + mInstallDependencyHelper.resolveLibraryDependenciesIfNeeded(pkg, callback); + callback.assertFailure(); + + assertThat(callback.error).hasMessageThat().contains( + "Failed to bind to Dependency Installer"); + } + + + @Test + public void testResolveLibraryDependenciesIfNeeded_allDependenciesInstalled() throws Exception { + // Return an empty list as missing dependency + PackageLite pkg = getPackageLite(TEST_APP_USING_SDK1_AND_SDK2); + List<SharedLibraryInfo> missingDependency = Collections.emptyList(); + when(mSharedLibraries.collectMissingSharedLibraryInfos(eq(pkg))) + .thenReturn(missingDependency); + + CallbackHelper callback = new CallbackHelper(/*expectSuccess=*/ true); + mInstallDependencyHelper.resolveLibraryDependenciesIfNeeded(pkg, callback); + callback.assertSuccess(); + } + + private static class CallbackHelper implements OutcomeReceiver<Void, PackageManagerException> { + public PackageManagerException error; + + private final CountDownLatch mWait = new CountDownLatch(1); + private final boolean mExpectSuccess; + + CallbackHelper(boolean expectSuccess) { + mExpectSuccess = expectSuccess; + } + + @Override + public void onResult(Void result) { + if (!mExpectSuccess) { + fail("Expected to fail"); + } + mWait.countDown(); + } + + @Override + public void onError(@NonNull PackageManagerException e) { + if (mExpectSuccess) { + fail("Expected success but received: " + e); + } + error = e; + mWait.countDown(); + } + + void assertSuccess() throws Exception { + assertThat(mWait.await(1000, TimeUnit.MILLISECONDS)).isTrue(); + assertThat(error).isNull(); + } + + void assertFailure() throws Exception { + assertThat(mWait.await(1000, TimeUnit.MILLISECONDS)).isTrue(); + assertThat(error).isNotNull(); + } + + } + + private PackageLite getPackageLite(String apkFileName) throws Exception { + File apkFile = copyApkToTmpDir(TEST_APP_USING_SDK1_AND_SDK2); + ParseResult<ApkLite> result = ApkLiteParseUtils.parseApkLite( + ParseTypeImpl.forDefaultParsing().reset(), apkFile, 0); + assertThat(result.isError()).isFalse(); + ApkLite baseApk = result.getResult(); + + return new PackageLite(/*path=*/ null, baseApk.getPath(), baseApk, + /*splitNames=*/ null, /*isFeatureSplits=*/ null, /*usesSplitNames=*/ null, + /*configForSplit=*/ null, /*splitApkPaths=*/ null, + /*splitRevisionCodes=*/ null, baseApk.getTargetSdkVersion(), + /*requiredSplitTypes=*/ null, /*splitTypes=*/ null); + } + + private File copyApkToTmpDir(String apkFileName) throws Exception { + File outFile = temporaryFolder.newFile(apkFileName); + String apkFilePath = PUSH_FILE_DIR + apkFileName; + File apkFile = new File(apkFilePath); + assertThat(apkFile.exists()).isTrue(); + try (InputStream is = new FileInputStream(apkFile)) { + FileUtils.copyToFileOrThrow(is, outFile); + } + return outFile; + } + +} diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java index 591e8df1725b..71c60ad02794 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java @@ -742,10 +742,11 @@ public class StagingManagerTest { /* stagedSessionErrorMessage */ "no error", /* preVerifiedDomains */ null, /* verifierController */ null, - /* initialVerificationPolicy */ + /* initialVerificationPolicy */ PackageInstaller.VERIFICATION_POLICY_BLOCK_FAIL_CLOSED, /* currentVerificationPolicy */ - PackageInstaller.VERIFICATION_POLICY_BLOCK_FAIL_CLOSED); + PackageInstaller.VERIFICATION_POLICY_BLOCK_FAIL_CLOSED, + /* installDependencyHelper */ null); StagingManager.StagedSession stagedSession = spy(session.mStagedSession); doReturn(packageName).when(stagedSession).getPackageName(); diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml index c645c0852f1b..9b7bbe04132c 100644 --- a/services/tests/servicestests/AndroidManifest.xml +++ b/services/tests/servicestests/AndroidManifest.xml @@ -114,6 +114,7 @@ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.CREATE_VIRTUAL_DEVICE" /> + <uses-permission android:name="android.permission.MANAGE_KEY_GESTURES" /> <queries> <package android:name="com.android.servicestests.apps.suspendtestapp" /> 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 2edde9b74d0a..d5b930769e43 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java @@ -33,6 +33,7 @@ import static com.android.internal.accessibility.AccessibilityShortcutController import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME; 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.KEY_GESTURE; import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.QUICK_SETTINGS; import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE; import static com.android.internal.accessibility.dialog.AccessibilityButtonChooserActivity.EXTRA_TYPE_TO_CHOOSE; @@ -80,6 +81,7 @@ import android.content.pm.ServiceInfo; import android.content.res.XmlResourceParser; import android.graphics.drawable.Icon; import android.hardware.display.DisplayManagerGlobal; +import android.hardware.input.KeyGestureEvent; import android.net.Uri; import android.os.Build; import android.os.Bundle; @@ -2183,6 +2185,168 @@ public class AccessibilityManagerServiceTest { verify(mockUserContext).getSystemService(EnhancedConfirmationManager.class); } + @Test + @EnableFlags(com.android.hardware.input.Flags.FLAG_ENABLE_TALKBACK_AND_MAGNIFIER_KEY_GESTURES) + public void handleKeyGestureEvent_toggleMagnifier() { + mFakePermissionEnforcer.grant(Manifest.permission.MANAGE_ACCESSIBILITY); + assertThat(ShortcutUtils.getShortcutTargetsFromSettings(mTestableContext, KEY_GESTURE, + mA11yms.getCurrentUserIdLocked())).isEmpty(); + + mA11yms.handleKeyGestureEvent(new KeyGestureEvent.Builder().setKeyGestureType( + KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION).setAction( + KeyGestureEvent.ACTION_GESTURE_COMPLETE).build()); + + assertThat(ShortcutUtils.getShortcutTargetsFromSettings(mTestableContext, KEY_GESTURE, + mA11yms.getCurrentUserIdLocked())).containsExactly(MAGNIFICATION_CONTROLLER_NAME); + + // The magnifier will only be toggled on the second event received since the first is + // used to toggle the feature on. + mA11yms.handleKeyGestureEvent(new KeyGestureEvent.Builder().setKeyGestureType( + KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION).setAction( + KeyGestureEvent.ACTION_GESTURE_COMPLETE).build()); + + verify(mInputFilter).notifyMagnificationShortcutTriggered(anyInt()); + } + + @Test + @EnableFlags(com.android.hardware.input.Flags.FLAG_ENABLE_TALKBACK_AND_MAGNIFIER_KEY_GESTURES) + public void handleKeyGestureEvent_activateSelectToSpeak_trustedService() { + setupAccessibilityServiceConnection(FLAG_REQUEST_ACCESSIBILITY_BUTTON); + mFakePermissionEnforcer.grant(Manifest.permission.MANAGE_ACCESSIBILITY); + + final AccessibilityServiceInfo trustedService = mockAccessibilityServiceInfo( + new ComponentName("package_a", "class_a"), + /* isSystemApp= */ true, /* isAlwaysOnService= */ true); + AccessibilityUserState userState = mA11yms.getCurrentUserState(); + userState.mInstalledServices.add(trustedService); + mTestableContext.getOrCreateTestableResources().addOverride( + R.string.config_defaultSelectToSpeakService, + trustedService.getComponentName().flattenToString()); + mTestableContext.getOrCreateTestableResources().addOverride( + R.array.config_trustedAccessibilityServices, + new String[]{trustedService.getComponentName().flattenToString()}); + + assertThat(ShortcutUtils.getShortcutTargetsFromSettings(mTestableContext, KEY_GESTURE, + mA11yms.getCurrentUserIdLocked())).isEmpty(); + + mA11yms.handleKeyGestureEvent(new KeyGestureEvent.Builder().setKeyGestureType( + KeyGestureEvent.KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK).setAction( + KeyGestureEvent.ACTION_GESTURE_COMPLETE).build()); + + assertThat(ShortcutUtils.getShortcutTargetsFromSettings(mTestableContext, KEY_GESTURE, + mA11yms.getCurrentUserIdLocked())).containsExactly( + trustedService.getComponentName().flattenToString()); + } + + @Test + @EnableFlags(com.android.hardware.input.Flags.FLAG_ENABLE_TALKBACK_AND_MAGNIFIER_KEY_GESTURES) + public void handleKeyGestureEvent_activateSelectToSpeak_preinstalledService() { + setupAccessibilityServiceConnection(FLAG_REQUEST_ACCESSIBILITY_BUTTON); + mFakePermissionEnforcer.grant(Manifest.permission.MANAGE_ACCESSIBILITY); + + final AccessibilityServiceInfo untrustedService = mockAccessibilityServiceInfo( + new ComponentName("package_a", "class_a"), + /* isSystemApp= */ true, /* isAlwaysOnService= */ true); + AccessibilityUserState userState = mA11yms.getCurrentUserState(); + userState.mInstalledServices.add(untrustedService); + mTestableContext.getOrCreateTestableResources().addOverride( + R.string.config_defaultSelectToSpeakService, + untrustedService.getComponentName().flattenToString()); + + assertThat(ShortcutUtils.getShortcutTargetsFromSettings(mTestableContext, KEY_GESTURE, + mA11yms.getCurrentUserIdLocked())).isEmpty(); + + mA11yms.handleKeyGestureEvent(new KeyGestureEvent.Builder().setKeyGestureType( + KeyGestureEvent.KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK).setAction( + KeyGestureEvent.ACTION_GESTURE_COMPLETE).build()); + + assertThat(ShortcutUtils.getShortcutTargetsFromSettings(mTestableContext, KEY_GESTURE, + mA11yms.getCurrentUserIdLocked())).isEmpty(); + } + + @Test + @EnableFlags(com.android.hardware.input.Flags.FLAG_ENABLE_TALKBACK_AND_MAGNIFIER_KEY_GESTURES) + public void handleKeyGestureEvent_activateSelectToSpeak_downloadedService() { + mFakePermissionEnforcer.grant(Manifest.permission.MANAGE_ACCESSIBILITY); + + final AccessibilityServiceInfo downloadedService = mockAccessibilityServiceInfo( + new ComponentName("package_a", "class_a"), + /* isSystemApp= */ false, /* isAlwaysOnService= */ true); + AccessibilityUserState userState = mA11yms.getCurrentUserState(); + userState.mInstalledServices.add(downloadedService); + mTestableContext.getOrCreateTestableResources().addOverride( + R.string.config_defaultSelectToSpeakService, + downloadedService.getComponentName().flattenToString()); + mTestableContext.getOrCreateTestableResources().addOverride( + R.array.config_trustedAccessibilityServices, + new String[]{downloadedService.getComponentName().flattenToString()}); + + assertThat(ShortcutUtils.getShortcutTargetsFromSettings(mTestableContext, KEY_GESTURE, + mA11yms.getCurrentUserIdLocked())).isEmpty(); + + mA11yms.handleKeyGestureEvent(new KeyGestureEvent.Builder().setKeyGestureType( + KeyGestureEvent.KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK).setAction( + KeyGestureEvent.ACTION_GESTURE_COMPLETE).build()); + + assertThat(ShortcutUtils.getShortcutTargetsFromSettings(mTestableContext, KEY_GESTURE, + mA11yms.getCurrentUserIdLocked())).isEmpty(); + } + + @Test + @EnableFlags(com.android.hardware.input.Flags.FLAG_ENABLE_TALKBACK_AND_MAGNIFIER_KEY_GESTURES) + public void handleKeyGestureEvent_activateSelectToSpeak_defaultNotInstalled() { + mFakePermissionEnforcer.grant(Manifest.permission.MANAGE_ACCESSIBILITY); + + final AccessibilityServiceInfo installedService = mockAccessibilityServiceInfo( + new ComponentName("package_a", "class_a"), + /* isSystemApp= */ true, /* isAlwaysOnService= */ true); + final AccessibilityServiceInfo defaultService = mockAccessibilityServiceInfo( + new ComponentName("package_b", "class_b"), + /* isSystemApp= */ true, /* isAlwaysOnService= */ true); + AccessibilityUserState userState = mA11yms.getCurrentUserState(); + userState.mInstalledServices.add(installedService); + mTestableContext.getOrCreateTestableResources().addOverride( + R.string.config_defaultSelectToSpeakService, + defaultService.getComponentName().flattenToString()); + mTestableContext.getOrCreateTestableResources().addOverride( + R.array.config_trustedAccessibilityServices, + new String[]{defaultService.getComponentName().flattenToString()}); + + assertThat(ShortcutUtils.getShortcutTargetsFromSettings(mTestableContext, KEY_GESTURE, + mA11yms.getCurrentUserIdLocked())).isEmpty(); + + mA11yms.handleKeyGestureEvent(new KeyGestureEvent.Builder().setKeyGestureType( + KeyGestureEvent.KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK).setAction( + KeyGestureEvent.ACTION_GESTURE_COMPLETE).build()); + + assertThat(ShortcutUtils.getShortcutTargetsFromSettings(mTestableContext, KEY_GESTURE, + mA11yms.getCurrentUserIdLocked())).isEmpty(); + } + + @Test + @EnableFlags(com.android.hardware.input.Flags.FLAG_ENABLE_TALKBACK_AND_MAGNIFIER_KEY_GESTURES) + public void handleKeyGestureEvent_activateSelectToSpeak_noDefault() { + mFakePermissionEnforcer.grant(Manifest.permission.MANAGE_ACCESSIBILITY); + + final AccessibilityServiceInfo installedService = mockAccessibilityServiceInfo( + new ComponentName("package_a", "class_a"), + /* isSystemApp= */ true, /* isAlwaysOnService= */ true); + AccessibilityUserState userState = mA11yms.getCurrentUserState(); + userState.mInstalledServices.add(installedService); + mTestableContext.getOrCreateTestableResources().addOverride( + R.array.config_trustedAccessibilityServices, + new String[]{installedService.getComponentName().flattenToString()}); + + assertThat(ShortcutUtils.getShortcutTargetsFromSettings(mTestableContext, KEY_GESTURE, + mA11yms.getCurrentUserIdLocked())).isEmpty(); + + mA11yms.handleKeyGestureEvent(new KeyGestureEvent.Builder().setKeyGestureType( + KeyGestureEvent.KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK).setAction( + KeyGestureEvent.ACTION_GESTURE_COMPLETE).build()); + + assertThat(ShortcutUtils.getShortcutTargetsFromSettings(mTestableContext, KEY_GESTURE, + mA11yms.getCurrentUserIdLocked())).isEmpty(); + } private Set<String> readStringsFromSetting(String setting) { final Set<String> result = new ArraySet<>(); @@ -2298,6 +2462,10 @@ public class AccessibilityManagerServiceTest { AccessibilityManagerService service) { super(context, service); } + + @Override + void notifyMagnificationShortcutTriggered(int displayId) { + } } private static class A11yTestableContext extends TestableContext { diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java index 8c35925debff..cb52eef6adfe 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java @@ -32,6 +32,7 @@ import static com.android.internal.accessibility.AccessibilityShortcutController import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.ALL; 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.KEY_GESTURE; import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.QUICK_SETTINGS; import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE; import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.TRIPLETAP; @@ -174,6 +175,7 @@ public class AccessibilityUserStateTest { mUserState.updateShortcutTargetsLocked(Set.of(componentNameString), SOFTWARE); mUserState.updateShortcutTargetsLocked(Set.of(componentNameString), GESTURE); mUserState.updateShortcutTargetsLocked(Set.of(componentNameString), QUICK_SETTINGS); + mUserState.updateShortcutTargetsLocked(Set.of(componentNameString), KEY_GESTURE); mUserState.updateA11yTilesInQsPanelLocked( Set.of(AccessibilityShortcutController.COLOR_INVERSION_TILE_COMPONENT_NAME)); mUserState.setTargetAssignedToAccessibilityButton(componentNameString); @@ -201,6 +203,7 @@ public class AccessibilityUserStateTest { assertTrue(mUserState.getShortcutTargetsLocked(SOFTWARE).isEmpty()); assertTrue(mUserState.getShortcutTargetsLocked(GESTURE).isEmpty()); assertTrue(mUserState.getShortcutTargetsLocked(QUICK_SETTINGS).isEmpty()); + assertTrue(mUserState.getShortcutTargetsLocked(KEY_GESTURE).isEmpty()); assertTrue(mUserState.getA11yQsTilesInQsPanel().isEmpty()); assertNull(mUserState.getTargetAssignedToAccessibilityButton()); assertFalse(mUserState.isTouchExplorationEnabledLocked()); 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 8164ef9314d9..f0d3456a39de 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 @@ -19,9 +19,13 @@ package com.android.server.accessibility.magnification; import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN; import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW; +import static com.android.internal.accessibility.common.MagnificationConstants.SCALE_MAX_VALUE; +import static com.android.internal.accessibility.common.MagnificationConstants.SCALE_MIN_VALUE; import static com.android.server.accessibility.AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID; import static com.android.server.wm.WindowManagerInternal.AccessibilityControllerInternal.UiChangesForAccessibilityCallbacks; +import static com.google.common.truth.Truth.assertThat; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -660,6 +664,90 @@ public class MagnificationControllerTest { } @Test + public void scaleMagnificationByStep_fullscreenMode_stepInAndOut() throws RemoteException { + setMagnificationEnabled(MODE_FULLSCREEN); + mMagnificationController.onPerformScaleAction(TEST_DISPLAY, 1.0f, false); + reset(mScreenMagnificationController); + + // Verify the zoom scale factor increases by + // {@code MagnificationController.DefaultMagnificationScaleStepProvider + // .ZOOM_STEP_SCALE_FACTOR} and the center coordinates are + // unchanged (Float.NaN as values denotes unchanged center). + mMagnificationController.scaleMagnificationByStep(TEST_DISPLAY, + MagnificationController.ZOOM_DIRECTION_IN); + verify(mScreenMagnificationController).setScaleAndCenter(eq(TEST_DISPLAY), + eq(MagnificationController + .DefaultMagnificationScaleStepProvider.ZOOM_STEP_SCALE_FACTOR), + eq(Float.NaN), eq(Float.NaN), anyBoolean(), anyInt()); + + mMagnificationController.scaleMagnificationByStep(TEST_DISPLAY, + MagnificationController.ZOOM_DIRECTION_OUT); + verify(mScreenMagnificationController).setScaleAndCenter(eq(TEST_DISPLAY), + eq(SCALE_MIN_VALUE), eq(Float.NaN), eq(Float.NaN), anyBoolean(), anyInt()); + } + + @Test + public void scaleMagnificationByStep_testMaxScaling() throws RemoteException { + setMagnificationEnabled(MODE_FULLSCREEN); + mMagnificationController.onPerformScaleAction(TEST_DISPLAY, SCALE_MIN_VALUE, false); + reset(mScreenMagnificationController); + + float currentScale = mScreenMagnificationController.getScale(TEST_DISPLAY); + while (currentScale < SCALE_MAX_VALUE) { + assertThat(mMagnificationController.scaleMagnificationByStep(TEST_DISPLAY, + MagnificationController.ZOOM_DIRECTION_IN)).isTrue(); + final float nextScale = mScreenMagnificationController.getScale(TEST_DISPLAY); + assertThat(nextScale).isGreaterThan(currentScale); + currentScale = nextScale; + } + + assertThat(currentScale).isEqualTo(SCALE_MAX_VALUE); + assertThat(mMagnificationController.scaleMagnificationByStep(TEST_DISPLAY, + MagnificationController.ZOOM_DIRECTION_IN)).isFalse(); + } + + @Test + public void scaleMagnificationByStep_testMinScaling() throws RemoteException { + setMagnificationEnabled(MODE_FULLSCREEN); + mMagnificationController.onPerformScaleAction(TEST_DISPLAY, SCALE_MAX_VALUE, false); + reset(mScreenMagnificationController); + + float currentScale = mScreenMagnificationController.getScale(TEST_DISPLAY); + while (currentScale > SCALE_MIN_VALUE) { + assertThat(mMagnificationController.scaleMagnificationByStep(TEST_DISPLAY, + MagnificationController.ZOOM_DIRECTION_OUT)).isTrue(); + final float nextScale = mScreenMagnificationController.getScale(TEST_DISPLAY); + assertThat(nextScale).isLessThan(currentScale); + currentScale = nextScale; + } + + assertThat(currentScale).isEqualTo(SCALE_MIN_VALUE); + assertThat(mMagnificationController.scaleMagnificationByStep(TEST_DISPLAY, + MagnificationController.ZOOM_DIRECTION_OUT)).isFalse(); + } + + @Test + public void scaleMagnificationByStep_windowedMode_stepInAndOut() throws RemoteException { + setMagnificationEnabled(MODE_WINDOW); + mMagnificationController.onPerformScaleAction(TEST_DISPLAY, SCALE_MIN_VALUE, false); + reset(mMagnificationConnectionManager); + + // Verify the zoom scale factor increases by + // {@code MagnificationController.DefaultMagnificationScaleStepProvider + // .ZOOM_STEP_SCALE_FACTOR}. + mMagnificationController.scaleMagnificationByStep(TEST_DISPLAY, + MagnificationController.ZOOM_DIRECTION_IN); + verify(mMagnificationConnectionManager).setScale(eq(TEST_DISPLAY), + eq(MagnificationController + .DefaultMagnificationScaleStepProvider.ZOOM_STEP_SCALE_FACTOR)); + + mMagnificationController.scaleMagnificationByStep(TEST_DISPLAY, + MagnificationController.ZOOM_DIRECTION_OUT); + verify(mMagnificationConnectionManager).setScale(eq(TEST_DISPLAY), + eq(SCALE_MIN_VALUE)); + } + + @Test public void enableWindowMode_notifyMagnificationChanged() throws RemoteException { setMagnificationEnabled(MODE_WINDOW); diff --git a/services/tests/servicestests/src/com/android/server/adaptiveauth/OWNERS b/services/tests/servicestests/src/com/android/server/adaptiveauth/OWNERS deleted file mode 100644 index 0218a7835586..000000000000 --- a/services/tests/servicestests/src/com/android/server/adaptiveauth/OWNERS +++ /dev/null @@ -1 +0,0 @@ -include /services/core/java/com/android/server/adaptiveauth/OWNERS
\ No newline at end of file diff --git a/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java b/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java index c418151b9946..96c6cbc9ec64 100644 --- a/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java @@ -123,7 +123,7 @@ public class AudioServiceTest { Assert.assertEquals("mic mute reporting wrong value", muted, mAudioService.isMicrophoneMuted()); // verify the intent for mic mute changed is supposed to be fired - Thread.sleep(MAX_MESSAGE_HANDLING_DELAY_MS); + mTestLooper.dispatchAll(); verify(mSpySystemServer, times(1)) .sendMicrophoneMuteChangedIntent(); reset(mSpySystemServer); @@ -148,7 +148,7 @@ public class AudioServiceTest { Assert.assertEquals("mic mute reporting wrong value", !muted, mAudioService.isMicrophoneMuted()); // verify the intent for mic mute changed is supposed to be fired - Thread.sleep(MAX_MESSAGE_HANDLING_DELAY_MS); + mTestLooper.dispatchAll(); verify(mSpySystemServer, times(1)) .sendMicrophoneMuteChangedIntent(); reset(mSpySystemServer); @@ -159,8 +159,7 @@ public class AudioServiceTest { public void testRingNotifAlias() throws Exception { Log.i(TAG, "running testRingNotifAlias"); Assert.assertNotNull(mAudioService); - // TODO add initialization message that can be caught here instead of sleeping - Thread.sleep(MAX_MESSAGE_HANDLING_DELAY_MS); // wait for full AudioService initialization + mTestLooper.dispatchAll(); // wait for full AudioService initialization // test with aliasing RING and NOTIFICATION mAudioService.setNotifAliasRingForTest(true); @@ -171,7 +170,7 @@ public class AudioServiceTest { mAudioService.setStreamVolume(AudioSystem.STREAM_NOTIFICATION, ringVol, 0, "bla"); mAudioService.setStreamVolume(AudioSystem.STREAM_RING, ringMaxVol, 0, "bla"); - Thread.sleep(MAX_MESSAGE_HANDLING_DELAY_MS); + mTestLooper.dispatchAll(); Assert.assertEquals(ringMaxVol, mAudioService.getStreamVolume(AudioSystem.STREAM_NOTIFICATION)); diff --git a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java index bc410d9168a9..88829c1a99b3 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java @@ -62,6 +62,7 @@ import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricConstants; import android.hardware.biometrics.BiometricManager; import android.hardware.biometrics.BiometricPrompt; +import android.hardware.biometrics.BiometricStateListener; import android.hardware.biometrics.Flags; import android.hardware.biometrics.IBiometricAuthenticator; import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback; @@ -70,8 +71,16 @@ import android.hardware.biometrics.IBiometricService; import android.hardware.biometrics.IBiometricServiceReceiver; import android.hardware.biometrics.IBiometricSysuiReceiver; import android.hardware.biometrics.PromptInfo; +import android.hardware.biometrics.SensorProperties; import android.hardware.display.DisplayManagerGlobal; +import android.hardware.face.FaceManager; +import android.hardware.face.FaceSensorProperties; +import android.hardware.face.FaceSensorPropertiesInternal; +import android.hardware.face.IFaceAuthenticatorsRegisteredCallback; import android.hardware.fingerprint.FingerprintManager; +import android.hardware.fingerprint.FingerprintSensorProperties; +import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; +import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback; import android.hardware.keymaster.HardwareAuthenticatorType; import android.os.Binder; import android.os.Handler; @@ -84,6 +93,7 @@ import android.platform.test.annotations.RequiresFlagsEnabled; import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.platform.test.flag.junit.SetFlagsRule; +import android.provider.Settings; import android.security.GateKeeper; import android.security.KeyStoreAuthorization; import android.service.gatekeeper.IGateKeeperService; @@ -93,6 +103,7 @@ import android.view.Display; import android.view.DisplayInfo; import android.view.WindowManager; +import androidx.test.core.app.ApplicationProvider; import androidx.test.filters.SmallTest; import com.android.internal.R; @@ -111,6 +122,7 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.ArrayList; import java.util.List; import java.util.Random; @@ -144,6 +156,27 @@ public class BiometricServiceTest { private static final int SENSOR_ID_FINGERPRINT = 0; private static final int SENSOR_ID_FACE = 1; + private final ArgumentCaptor<IFingerprintAuthenticatorsRegisteredCallback.Stub> + mFingerprintAuthenticatorRegisteredCallbackCaptor = ArgumentCaptor.forClass( + IFingerprintAuthenticatorsRegisteredCallback.Stub.class); + private final ArgumentCaptor<IFaceAuthenticatorsRegisteredCallback.Stub> + mFaceAuthenticatorRegisteredCallbackCaptor = ArgumentCaptor.forClass( + IFaceAuthenticatorsRegisteredCallback.Stub.class); + private final ArgumentCaptor<BiometricStateListener> mBiometricStateListenerArgumentCaptor = + ArgumentCaptor.forClass(BiometricStateListener.class); + private final List<FingerprintSensorPropertiesInternal> + mFingerprintSensorPropertiesInternals = List.of( + new FingerprintSensorPropertiesInternal(SENSOR_ID_FINGERPRINT, + SensorProperties.STRENGTH_STRONG, 5 /* maxEnrollmentsPerUser */, + List.of(), FingerprintSensorProperties.TYPE_UNKNOWN, + true /* resetLockoutRequiresHardwareAuthToken */)); + private final List<FaceSensorPropertiesInternal> + mFaceSensorPropertiesInternals = List.of( + new FaceSensorPropertiesInternal(SENSOR_ID_FACE, + SensorProperties.STRENGTH_STRONG, 5 /* maxEnrollmentsPerUser */, + List.of(), FaceSensorProperties.TYPE_UNKNOWN, + false /* supportsFaceDetection */, false /* supportsSelfIllumination */, + false /* resetLockoutRequiresChallenge */)); private BiometricService mBiometricService; @@ -192,6 +225,10 @@ public class BiometricServiceTest { @Mock private BiometricNotificationLogger mNotificationLogger; + @Mock + private FingerprintManager mFingerprintManager; + @Mock + private FaceManager mFaceManager; BiometricContextProvider mBiometricContextProvider; @@ -1975,6 +2012,59 @@ public class BiometricServiceTest { eq(hardwareAuthenticators)); } + @Test + public void testMandatoryBiometricsValue_whenParentProfileEnabled() throws RemoteException { + final Context context = ApplicationProvider.getApplicationContext(); + final int profileParentId = context.getContentResolver().getUserId(); + final int userId = profileParentId + 1; + final BiometricService.SettingObserver settingObserver = + new BiometricService.SettingObserver( + context, mBiometricHandlerProvider.getBiometricCallbackHandler(), + new ArrayList<>(), mUserManager, mFingerprintManager, mFaceManager); + + verify(mFingerprintManager).addAuthenticatorsRegisteredCallback( + mFingerprintAuthenticatorRegisteredCallbackCaptor.capture()); + verify(mFaceManager).addAuthenticatorsRegisteredCallback( + mFaceAuthenticatorRegisteredCallbackCaptor.capture()); + + mFingerprintAuthenticatorRegisteredCallbackCaptor.getValue().onAllAuthenticatorsRegistered( + mFingerprintSensorPropertiesInternals); + mFaceAuthenticatorRegisteredCallbackCaptor.getValue().onAllAuthenticatorsRegistered( + mFaceSensorPropertiesInternals); + + verify(mFingerprintManager).registerBiometricStateListener( + mBiometricStateListenerArgumentCaptor.capture()); + + mBiometricStateListenerArgumentCaptor.getValue().onEnrollmentsChanged(userId, + SENSOR_ID_FINGERPRINT, true /* hasEnrollments */); + + verify(mFaceManager).registerBiometricStateListener( + mBiometricStateListenerArgumentCaptor.capture()); + + mBiometricStateListenerArgumentCaptor.getValue().onEnrollmentsChanged(userId, + SENSOR_ID_FACE, true /* hasEnrollments */); + + when(mUserManager.getProfileParent(userId)).thenReturn(new UserInfo(profileParentId, + "", 0)); + when(mUserManager.getEnabledProfileIds(profileParentId)).thenReturn(new int[]{userId}); + + //Disable Identity Check for profile user + Settings.Secure.putIntForUser(context.getContentResolver(), + Settings.Secure.MANDATORY_BIOMETRICS, 0, userId); + Settings.Secure.putIntForUser(context.getContentResolver(), + Settings.Secure.MANDATORY_BIOMETRICS_REQUIREMENTS_SATISFIED, 0, + userId); + //Enable Identity Check for parent user + Settings.Secure.putIntForUser(context.getContentResolver(), + Settings.Secure.MANDATORY_BIOMETRICS, 1, profileParentId); + Settings.Secure.putIntForUser(context.getContentResolver(), + Settings.Secure.MANDATORY_BIOMETRICS_REQUIREMENTS_SATISFIED, 1, + profileParentId); + + assertTrue(settingObserver.getMandatoryBiometricsEnabledAndRequirementsSatisfiedForUser( + userId)); + } + // Helper methods private int invokeCanAuthenticate(BiometricService service, int authenticators) diff --git a/services/tests/servicestests/src/com/android/server/biometrics/PreAuthInfoTest.java b/services/tests/servicestests/src/com/android/server/biometrics/PreAuthInfoTest.java index cf628add705a..b81bf3c6e712 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/PreAuthInfoTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/PreAuthInfoTest.java @@ -332,6 +332,28 @@ public class PreAuthInfoTest { assertThat(preAuthInfo.callingUserId).isEqualTo(USER_ID); } + @Test + @RequiresFlagsEnabled(Flags.FLAG_EFFECTIVE_USER_BP) + public void testCredentialOwnerIdAsUserId_forMandatoryBiometrics() throws Exception { + when(mUserManager.getCredentialOwnerProfile(USER_ID)).thenReturn(OWNER_ID); + when(mSettingObserver.getMandatoryBiometricsEnabledAndRequirementsSatisfiedForUser( + OWNER_ID)).thenReturn(true); + when(mSettingObserver.getMandatoryBiometricsEnabledAndRequirementsSatisfiedForUser( + USER_ID)).thenReturn(false); + when(mTrustManager.isInSignificantPlace()).thenReturn(false); + + final BiometricSensor sensor = getFaceSensor(); + final PromptInfo promptInfo = new PromptInfo(); + promptInfo.setAuthenticators(BiometricManager.Authenticators.IDENTITY_CHECK); + promptInfo.setNegativeButtonText(TEST_PACKAGE_NAME); + final PreAuthInfo preAuthInfo = PreAuthInfo.create(mTrustManager, mDevicePolicyManager, + mSettingObserver, List.of(sensor), USER_ID , promptInfo, TEST_PACKAGE_NAME, + false /* checkDevicePolicyManager */, mContext, mBiometricCameraManager, + mUserManager); + + assertThat(preAuthInfo.getIsMandatoryBiometricsAuthentication()).isTrue(); + } + private BiometricSensor getFingerprintSensor() { BiometricSensor sensor = new BiometricSensor(mContext, SENSOR_ID_FINGERPRINT, TYPE_FINGERPRINT, BiometricManager.Authenticators.BIOMETRIC_STRONG, diff --git a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java index 93aa10b9112f..fd221185bacf 100644 --- a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java +++ b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java @@ -68,7 +68,6 @@ import androidx.test.InstrumentationRegistry; import com.android.internal.R; import com.android.server.compat.PlatformCompat; -import com.android.server.integrity.model.IntegrityCheckResult; import com.android.server.testutils.TestUtils; import org.junit.After; diff --git a/services/tests/servicestests/src/com/android/server/integrity/model/ByteTrackedOutputStreamTest.java b/services/tests/servicestests/src/com/android/server/integrity/model/ByteTrackedOutputStreamTest.java deleted file mode 100644 index 57274bf13499..000000000000 --- a/services/tests/servicestests/src/com/android/server/integrity/model/ByteTrackedOutputStreamTest.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.integrity.model; - -import static com.google.common.truth.Truth.assertThat; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -import java.io.ByteArrayOutputStream; - -@RunWith(JUnit4.class) -public class ByteTrackedOutputStreamTest { - - @Test - public void testConstructorStartsWithZeroBytesWritten() { - ByteTrackedOutputStream byteTrackedOutputStream = - new ByteTrackedOutputStream(new ByteArrayOutputStream()); - - assertThat(byteTrackedOutputStream.getWrittenBytesCount()).isEqualTo(0); - } - - @Test - public void testSuccessfulWriteAndValidateWrittenBytesCount_directFromByteArray() - throws Exception { - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - ByteTrackedOutputStream byteTrackedOutputStream = new ByteTrackedOutputStream(outputStream); - - byte[] outputContent = "This is going to be outputed for tests.".getBytes(); - byteTrackedOutputStream.write(outputContent); - - assertThat(byteTrackedOutputStream.getWrittenBytesCount()).isEqualTo(outputContent.length); - assertThat(outputStream.toByteArray().length).isEqualTo(outputContent.length); - } - - @Test - public void testSuccessfulWriteAndValidateWrittenBytesCount_fromBitStream() throws Exception { - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - ByteTrackedOutputStream byteTrackedOutputStream = new ByteTrackedOutputStream(outputStream); - - BitOutputStream bitOutputStream = new BitOutputStream(byteTrackedOutputStream); - bitOutputStream.setNext(/* numOfBits= */5, /* value= */1); - bitOutputStream.flush(); - - // Even though we wrote 5 bits, this will complete to 1 byte. - assertThat(byteTrackedOutputStream.getWrittenBytesCount()).isEqualTo(1); - - // Add a bit less than 2 bytes (10 bits). - bitOutputStream.setNext(/* numOfBits= */10, /* value= */1); - bitOutputStream.flush(); - assertThat(byteTrackedOutputStream.getWrittenBytesCount()).isEqualTo(3); - - assertThat(outputStream.toByteArray().length).isEqualTo(3); - } -} diff --git a/services/tests/servicestests/src/com/android/server/integrity/model/IntegrityCheckResultTest.java b/services/tests/servicestests/src/com/android/server/integrity/model/IntegrityCheckResultTest.java deleted file mode 100644 index d31ed689a7da..000000000000 --- a/services/tests/servicestests/src/com/android/server/integrity/model/IntegrityCheckResultTest.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.integrity.model; - -import static com.google.common.truth.Truth.assertThat; - -import android.content.integrity.AtomicFormula; -import android.content.integrity.CompoundFormula; -import android.content.integrity.Rule; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -import java.util.Arrays; -import java.util.Collections; - -@RunWith(JUnit4.class) -public class IntegrityCheckResultTest { - - @Test - public void createAllowResult() { - IntegrityCheckResult allowResult = IntegrityCheckResult.allow(); - - assertThat(allowResult.getEffect()).isEqualTo(IntegrityCheckResult.Effect.ALLOW); - assertThat(allowResult.getMatchedRules()).isEmpty(); - } - - @Test - public void createAllowResultWithRule() { - String packageName = "com.test.deny"; - Rule forceAllowRule = - new Rule( - new AtomicFormula.StringAtomicFormula(AtomicFormula.PACKAGE_NAME, - packageName), - Rule.FORCE_ALLOW); - - IntegrityCheckResult allowResult = - IntegrityCheckResult.allow(Collections.singletonList(forceAllowRule)); - - assertThat(allowResult.getEffect()).isEqualTo(IntegrityCheckResult.Effect.ALLOW); - assertThat(allowResult.getMatchedRules()).containsExactly(forceAllowRule); - } - - @Test - public void createDenyResultWithRule() { - String packageName = "com.test.deny"; - Rule failedRule = - new Rule( - new AtomicFormula.StringAtomicFormula(AtomicFormula.PACKAGE_NAME, - packageName), - Rule.DENY); - - IntegrityCheckResult denyResult = - IntegrityCheckResult.deny(Collections.singletonList(failedRule)); - - assertThat(denyResult.getEffect()).isEqualTo(IntegrityCheckResult.Effect.DENY); - assertThat(denyResult.getMatchedRules()).containsExactly(failedRule); - } - - @Test - public void isDenyCausedByAppCertificate() { - String packageName = "com.test.deny"; - String appCert = "app-cert"; - Rule failedRule = - new Rule( - new CompoundFormula( - CompoundFormula.AND, - Arrays.asList( - new AtomicFormula.StringAtomicFormula( - AtomicFormula.PACKAGE_NAME, packageName), - new AtomicFormula.StringAtomicFormula( - AtomicFormula.APP_CERTIFICATE, appCert))), - Rule.DENY); - Rule otherFailedRule = - new Rule( - new AtomicFormula.LongAtomicFormula(AtomicFormula.VERSION_CODE, - AtomicFormula.EQ, 12), - Rule.DENY); - - IntegrityCheckResult denyResult = - IntegrityCheckResult.deny(Arrays.asList(failedRule, otherFailedRule)); - - assertThat(denyResult.isCausedByAppCertRule()).isTrue(); - assertThat(denyResult.isCausedByInstallerRule()).isFalse(); - } - - @Test - public void isDenyCausedByInstaller() { - String packageName = "com.test.deny"; - String appCert = "app-cert"; - Rule failedRule = - new Rule( - new CompoundFormula( - CompoundFormula.AND, - Arrays.asList( - new AtomicFormula.StringAtomicFormula( - AtomicFormula.PACKAGE_NAME, packageName), - new AtomicFormula.StringAtomicFormula( - AtomicFormula.INSTALLER_CERTIFICATE, appCert))), - Rule.DENY); - Rule otherFailedRule = - new Rule( - new AtomicFormula.LongAtomicFormula(AtomicFormula.VERSION_CODE, - AtomicFormula.EQ, 12), - Rule.DENY); - - IntegrityCheckResult denyResult = - IntegrityCheckResult.deny(Arrays.asList(failedRule, otherFailedRule)); - - assertThat(denyResult.isCausedByAppCertRule()).isFalse(); - assertThat(denyResult.isCausedByInstallerRule()).isTrue(); - } -} diff --git a/services/tests/servicestests/src/com/android/server/location/contexthub/ContextHubServiceTest.java b/services/tests/servicestests/src/com/android/server/location/contexthub/ContextHubServiceTest.java index 685e8d6a3bc5..e611867493eb 100644 --- a/services/tests/servicestests/src/com/android/server/location/contexthub/ContextHubServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/location/contexthub/ContextHubServiceTest.java @@ -65,7 +65,7 @@ public class ContextHubServiceTest { new Pair<>(Arrays.asList(mMockContextHubInfo), Arrays.asList("")); when(mMockContextHubInfo.getId()).thenReturn(CONTEXT_HUB_ID); when(mMockContextHubInfo.toString()).thenReturn(CONTEXT_HUB_STRING); - when(mMockContextHubWrapper.getHubs()).thenReturn(hubInfo); + when(mMockContextHubWrapper.getContextHubs()).thenReturn(hubInfo); when(mMockContextHubWrapper.supportsLocationSettingNotifications()).thenReturn(true); when(mMockContextHubWrapper.supportsWifiSettingNotifications()).thenReturn(true); diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java index dddab657be14..5a7027edc20d 100644 --- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java @@ -2158,13 +2158,11 @@ public class NetworkPolicyManagerServiceTest { } @Test - @RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE) public void testBackgroundChainEnabled() throws Exception { verify(mNetworkManager).setFirewallChainEnabled(FIREWALL_CHAIN_BACKGROUND, true); } @Test - @RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE) @RequiresFlagsDisabled(Flags.FLAG_USE_DIFFERENT_DELAYS_FOR_BACKGROUND_CHAIN) public void testBackgroundChainOnProcStateChangeSameDelay() throws Exception { // initialization calls setFirewallChainEnabled, so we want to reset the invocations. @@ -2194,10 +2192,7 @@ public class NetworkPolicyManagerServiceTest { } @Test - @RequiresFlagsEnabled({ - Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE, - Flags.FLAG_USE_DIFFERENT_DELAYS_FOR_BACKGROUND_CHAIN - }) + @RequiresFlagsEnabled(Flags.FLAG_USE_DIFFERENT_DELAYS_FOR_BACKGROUND_CHAIN) public void testBackgroundChainOnProcStateChangeDifferentDelays() throws Exception { // The app will be blocked when there is no prior proc-state. assertTrue(mService.isUidNetworkingBlocked(UID_A, false)); @@ -2247,7 +2242,6 @@ public class NetworkPolicyManagerServiceTest { } @Test - @RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE) public void testBackgroundChainOnAllowlistChange() throws Exception { // initialization calls setFirewallChainEnabled, so we want to reset the invocations. clearInvocations(mNetworkManager); @@ -2285,7 +2279,6 @@ public class NetworkPolicyManagerServiceTest { } @Test - @RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE) public void testBackgroundChainOnTempAllowlistChange() throws Exception { // initialization calls setFirewallChainEnabled, so we want to reset the invocations. clearInvocations(mNetworkManager); @@ -2387,7 +2380,6 @@ public class NetworkPolicyManagerServiceTest { } @Test - @RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE) public void testUidObserverFiltersProcStateChanges() throws Exception { int testProcStateSeq = 0; try (SyncBarrier b = new SyncBarrier(mService.mUidEventHandler)) { @@ -2450,7 +2442,6 @@ public class NetworkPolicyManagerServiceTest { } @Test - @RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE) public void testUidObserverFiltersStaleChanges() throws Exception { final int testProcStateSeq = 51; try (SyncBarrier b = new SyncBarrier(mService.mUidEventHandler)) { @@ -2470,7 +2461,6 @@ public class NetworkPolicyManagerServiceTest { } @Test - @RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE) public void testUidObserverFiltersCapabilityChanges() throws Exception { int testProcStateSeq = 0; try (SyncBarrier b = new SyncBarrier(mService.mUidEventHandler)) { @@ -2559,7 +2549,6 @@ public class NetworkPolicyManagerServiceTest { } @Test - @RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE) public void testObsoleteHandleUidChanged() throws Exception { callAndWaitOnUidGone(UID_A); assertTrue(mService.isUidNetworkingBlocked(UID_A, false)); diff --git a/services/tests/servicestests/src/com/android/server/adaptiveauth/AdaptiveAuthServiceTest.java b/services/tests/servicestests/src/com/android/server/security/adaptiveauthentication/AdaptiveAuthenticationServiceTest.java index d1806881ee37..154494a13072 100644 --- a/services/tests/servicestests/src/com/android/server/adaptiveauth/AdaptiveAuthServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/security/adaptiveauthentication/AdaptiveAuthenticationServiceTest.java @@ -14,14 +14,14 @@ * limitations under the License. */ -package com.android.server.adaptiveauth; +package com.android.server.security.adaptiveauthentication; import static android.adaptiveauth.Flags.FLAG_ENABLE_ADAPTIVE_AUTH; import static android.adaptiveauth.Flags.FLAG_REPORT_BIOMETRIC_AUTH_ATTEMPTS; import static android.security.Flags.FLAG_REPORT_PRIMARY_AUTH_ATTEMPTS; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST; -import static com.android.server.adaptiveauth.AdaptiveAuthService.MAX_ALLOWED_FAILED_AUTH_ATTEMPTS; +import static com.android.server.security.adaptiveauthentication.AdaptiveAuthenticationService.MAX_ALLOWED_FAILED_AUTH_ATTEMPTS; import static org.junit.Assert.assertEquals; import static org.junit.Assume.assumeTrue; @@ -66,12 +66,12 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; /** - * atest FrameworksServicesTests:AdaptiveAuthServiceTest + * atest FrameworksServicesTests:AdaptiveAuthenticationServiceTest */ @Presubmit @SmallTest @RunWith(AndroidJUnit4.class) -public class AdaptiveAuthServiceTest { +public class AdaptiveAuthenticationServiceTest { @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); @@ -81,7 +81,7 @@ public class AdaptiveAuthServiceTest { private static final int REASON_UNKNOWN = 0; // BiometricRequestConstants.RequestReason private Context mContext; - private AdaptiveAuthService mAdaptiveAuthService; + private AdaptiveAuthenticationService mAdaptiveAuthenticationService; @Mock LockPatternUtils mLockPatternUtils; @@ -124,8 +124,9 @@ public class AdaptiveAuthServiceTest { LocalServices.removeServiceForTest(UserManagerInternal.class); LocalServices.addService(UserManagerInternal.class, mUserManager); - mAdaptiveAuthService = new AdaptiveAuthService(mContext, mLockPatternUtils); - mAdaptiveAuthService.init(); + mAdaptiveAuthenticationService = new AdaptiveAuthenticationService( + mContext, mLockPatternUtils); + mAdaptiveAuthenticationService.init(); verify(mLockSettings).registerLockSettingsStateListener( mLockSettingsStateListenerCaptor.capture()); @@ -317,13 +318,13 @@ public class AdaptiveAuthServiceTest { private void verifyNotLockDevice(int expectedCntFailedAttempts, int userId) { assertEquals(expectedCntFailedAttempts, - mAdaptiveAuthService.mFailedAttemptsForUser.get(userId)); + mAdaptiveAuthenticationService.mFailedAttemptsForUser.get(userId)); verify(mWindowManager, never()).lockNow(); } private void verifyLockDevice(int userId) { assertEquals(MAX_ALLOWED_FAILED_AUTH_ATTEMPTS, - mAdaptiveAuthService.mFailedAttemptsForUser.get(userId)); + mAdaptiveAuthenticationService.mFailedAttemptsForUser.get(userId)); verify(mLockPatternUtils).requireStrongAuth( eq(SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST), eq(userId)); // If userId is MANAGED_PROFILE_USER_ID, the StrongAuthFlag of its parent (PRIMARY_USER_ID) diff --git a/services/tests/servicestests/src/com/android/server/security/adaptiveauthentication/OWNERS b/services/tests/servicestests/src/com/android/server/security/adaptiveauthentication/OWNERS new file mode 100644 index 000000000000..bc8efa92c16f --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/security/adaptiveauthentication/OWNERS @@ -0,0 +1 @@ +include /services/core/java/com/android/server/security/adaptiveauthentication/OWNERS
\ No newline at end of file diff --git a/services/tests/servicestests/src/com/android/server/stats/pull/psi/OWNERS b/services/tests/servicestests/src/com/android/server/stats/pull/psi/OWNERS new file mode 100644 index 000000000000..e068a845c670 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/stats/pull/psi/OWNERS @@ -0,0 +1 @@ +include /services/core/java/com/android/server/stats/pull/psi/OWNERS diff --git a/services/tests/servicestests/src/com/android/server/stats/pull/psi/PsiExtractorTest.java b/services/tests/servicestests/src/com/android/server/stats/pull/psi/PsiExtractorTest.java new file mode 100644 index 000000000000..b563c08f6be0 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/stats/pull/psi/PsiExtractorTest.java @@ -0,0 +1,305 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.stats.pull.psi; + +import static org.testng.AssertJUnit.assertEquals; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; + +@RunWith(AndroidJUnit4.class) + +public class PsiExtractorTest { + @Mock + private PsiExtractor.PsiReader mPsiReader; + private PsiExtractor mPsiExtractor; + // PSI file content with both some and full lines. + private static final String PSI_FILE_CONTENT_BOTH_LINES = + "some avg10=0.12 avg60=0.34 avg300=0.56 total=12345678\n" + + "full avg10=0.21 avg60=0.43 avg300=0.65 total=87654321"; + // PSI file content with only some line. + private static final String PSI_FILE_CONTENT_ONLY_SOME_LINE = + "some avg10=0.12 avg60=0.34 avg300=0.56 total=12345678"; + + // PSI file content with only full line. + private static final String PSI_FILE_CONTENT_ONLY_FULL_LINE = + "\nfull avg10=0.21 avg60=0.43 avg300=0.65 total=87654321"; + + // PSI file content that is malformed with "avg60" missing from the both lines. + private static final String BOTH_AVG60_MISSING_PSI_FILE_CONTENT = + "some avg10=0.12 avg300=0.56 total=12345678\n" + + "full avg10=0.21 avg300=0.65 total=87654321"; + + // PSI file content that is malformed with non number "avg10" from the both lines. + private static final String NON_NUM_AVG10_PSI_FILE_CONTENT = + "some avg10=1.a2 avg300=0.56 total=12345678\n" + + "full avg10=0.2s1 avg60=0.43 avg300=0.65 total=87654321"; + + // PSI file content that is malformed with non number "avg300" from the both lines. + private static final String NON_NUM_AVG300_PSI_FILE_CONTENT = + "some avg10=0.2 avg60=0.43 avg300=0.5ss6 total=12345678\n" + + "full avg10=0.21 avg60=0.43 avg300=0.6b5 total=87654321"; + + // PSI file content that is malformed with non number "total" from the both lines. + private static final String BOTH_TOTAL_MISSING_PSI_FILE_CONTENT = + "some avg10=0.2 avg60=0.43 avg300=0.56\n" + + "full avg10=0.21 avg60=0.43 avg300=0.65"; + + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + mPsiExtractor = new PsiExtractor(mPsiReader); + } + + @Test + public void getPsiData_bothLinesPresentedAndValidMemory() { + Mockito.when(mPsiReader.read("/proc/pressure/memory")).thenReturn( + PSI_FILE_CONTENT_BOTH_LINES); + PsiData psiData = mPsiExtractor.getPsiData(PsiData.ResourceType.MEMORY); + assertEquals(psiData.getResourceType(), PsiData.ResourceType.MEMORY); + assertEquals(psiData.getSomeAvg10SecPercentage(), (float) 0.12); + assertEquals(psiData.getSomeAvg60SecPercentage(), (float) 0.34); + assertEquals(psiData.getSomeAvg300SecPercentage(), (float) 0.56); + assertEquals(psiData.getSomeTotalUsec(), 12345678); + assertEquals(psiData.getFullAvg10SecPercentage(), (float) 0.21); + assertEquals(psiData.getFullAvg60SecPercentage(), (float) 0.43); + assertEquals(psiData.getFullAvg300SecPercentage(), (float) 0.65); + assertEquals(psiData.getFullTotalUsec(), 87654321); + } + + @Test + public void getPsiData_bothLinesPresentedAndValidCpu() { + Mockito.when(mPsiReader.read("/proc/pressure/cpu")).thenReturn( + PSI_FILE_CONTENT_BOTH_LINES); + PsiData psiData = mPsiExtractor.getPsiData(PsiData.ResourceType.CPU); + assertEquals(psiData.getResourceType(), PsiData.ResourceType.CPU); + assertEquals(psiData.getSomeAvg10SecPercentage(), (float) 0.12); + assertEquals(psiData.getSomeAvg60SecPercentage(), (float) 0.34); + assertEquals(psiData.getSomeAvg300SecPercentage(), (float) 0.56); + assertEquals(psiData.getSomeTotalUsec(), 12345678); + assertEquals(psiData.getFullAvg10SecPercentage(), (float) 0.21); + assertEquals(psiData.getFullAvg60SecPercentage(), (float) 0.43); + assertEquals(psiData.getFullAvg300SecPercentage(), (float) 0.65); + assertEquals(psiData.getFullTotalUsec(), 87654321); + } + + @Test + public void getPsiData_bothLinesPresentedAndValidIO() { + Mockito.when(mPsiReader.read("/proc/pressure/io")).thenReturn( + PSI_FILE_CONTENT_BOTH_LINES); + PsiData psiData = mPsiExtractor.getPsiData(PsiData.ResourceType.IO); + assertEquals(psiData.getResourceType(), PsiData.ResourceType.IO); + assertEquals(psiData.getSomeAvg10SecPercentage(), (float) 0.12); + assertEquals(psiData.getSomeAvg60SecPercentage(), (float) 0.34); + assertEquals(psiData.getSomeAvg300SecPercentage(), (float) 0.56); + assertEquals(psiData.getSomeTotalUsec(), 12345678); + assertEquals(psiData.getFullAvg10SecPercentage(), (float) 0.21); + assertEquals(psiData.getFullAvg60SecPercentage(), (float) 0.43); + assertEquals(psiData.getFullAvg300SecPercentage(), (float) 0.65); + assertEquals(psiData.getFullTotalUsec(), 87654321); + } + + @Test + public void getPsiData_onlySomePresentedAndValidMemory() { + Mockito.when(mPsiReader.read("/proc/pressure/memory")).thenReturn( + PSI_FILE_CONTENT_ONLY_SOME_LINE); + PsiData psiData = mPsiExtractor.getPsiData(PsiData.ResourceType.MEMORY); + assertEquals(psiData.getResourceType(), PsiData.ResourceType.MEMORY); + assertEquals(psiData.getSomeAvg10SecPercentage(), (float) 0.12); + assertEquals(psiData.getSomeAvg60SecPercentage(), (float) 0.34); + assertEquals(psiData.getSomeAvg300SecPercentage(), (float) 0.56); + assertEquals(psiData.getSomeTotalUsec(), 12345678); + assertEquals(psiData.getFullAvg10SecPercentage(), (float) -1.0); + assertEquals(psiData.getFullAvg60SecPercentage(), (float) -1.0); + assertEquals(psiData.getFullAvg300SecPercentage(), (float) -1.0); + assertEquals(psiData.getFullTotalUsec(), -1); + } + + @Test + public void getPsiData_onlySomePresentedAndValidCpu() { + Mockito.when(mPsiReader.read("/proc/pressure/cpu")).thenReturn( + PSI_FILE_CONTENT_ONLY_SOME_LINE); + PsiData psiData = mPsiExtractor.getPsiData(PsiData.ResourceType.CPU); + assertEquals(psiData.getResourceType(), PsiData.ResourceType.CPU); + assertEquals(psiData.getSomeAvg10SecPercentage(), (float) 0.12); + assertEquals(psiData.getSomeAvg60SecPercentage(), (float) 0.34); + assertEquals(psiData.getSomeAvg300SecPercentage(), (float) 0.56); + assertEquals(psiData.getSomeTotalUsec(), 12345678); + assertEquals(psiData.getFullAvg10SecPercentage(), (float) 0.0); + assertEquals(psiData.getFullAvg60SecPercentage(), (float) 0.0); + assertEquals(psiData.getFullAvg300SecPercentage(), (float) 0.0); + assertEquals(psiData.getFullTotalUsec(), 0); + } + + @Test + public void getPsiData_onlySomePresentedAndValidIO() { + Mockito.when(mPsiReader.read("/proc/pressure/io")).thenReturn( + PSI_FILE_CONTENT_ONLY_SOME_LINE); + PsiData psiData = mPsiExtractor.getPsiData(PsiData.ResourceType.IO); + assertEquals(psiData.getResourceType(), PsiData.ResourceType.IO); + assertEquals(psiData.getSomeAvg10SecPercentage(), (float) 0.12); + assertEquals(psiData.getSomeAvg60SecPercentage(), (float) 0.34); + assertEquals(psiData.getSomeAvg300SecPercentage(), (float) 0.56); + assertEquals(psiData.getSomeTotalUsec(), 12345678); + assertEquals(psiData.getFullAvg10SecPercentage(), (float) -1.0); + assertEquals(psiData.getFullAvg60SecPercentage(), (float) -1.0); + assertEquals(psiData.getFullAvg300SecPercentage(), (float) -1.0); + assertEquals(psiData.getFullTotalUsec(), -1); + } + + @Test + public void getPsiData_onlyFullPresentedAndValidMemory() { + Mockito.when(mPsiReader.read("/proc/pressure/memory")).thenReturn( + PSI_FILE_CONTENT_ONLY_FULL_LINE); + PsiData psiData = mPsiExtractor.getPsiData(PsiData.ResourceType.MEMORY); + assertEquals(psiData.getResourceType(), PsiData.ResourceType.MEMORY); + assertEquals(psiData.getSomeAvg10SecPercentage(), (float) -1.0); + assertEquals(psiData.getSomeAvg60SecPercentage(), (float) -1.0); + assertEquals(psiData.getSomeAvg300SecPercentage(), (float) -1.0); + assertEquals(psiData.getSomeTotalUsec(), -1); + assertEquals(psiData.getFullAvg10SecPercentage(), (float) 0.21); + assertEquals(psiData.getFullAvg60SecPercentage(), (float) 0.43); + assertEquals(psiData.getFullAvg300SecPercentage(), (float) 0.65); + assertEquals(psiData.getFullTotalUsec(), 87654321); + } + + @Test + public void getPsiData_onlyFullPresentedAndValidCpu() { + Mockito.when(mPsiReader.read("/proc/pressure/cpu")).thenReturn( + PSI_FILE_CONTENT_ONLY_FULL_LINE); + PsiData psiData = mPsiExtractor.getPsiData(PsiData.ResourceType.CPU); + assertEquals(psiData.getResourceType(), PsiData.ResourceType.CPU); + assertEquals(psiData.getSomeAvg10SecPercentage(), (float) -1.0); + assertEquals(psiData.getSomeAvg60SecPercentage(), (float) -1.0); + assertEquals(psiData.getSomeAvg300SecPercentage(), (float) -1.0); + assertEquals(psiData.getSomeTotalUsec(), -1); + assertEquals(psiData.getFullAvg10SecPercentage(), (float) 0.21); + assertEquals(psiData.getFullAvg60SecPercentage(), (float) 0.43); + assertEquals(psiData.getFullAvg300SecPercentage(), (float) 0.65); + assertEquals(psiData.getFullTotalUsec(), 87654321); + } + + @Test + public void getPsiData_onlyFullPresentedAndValidIO() { + Mockito.when(mPsiReader.read("/proc/pressure/io")).thenReturn( + PSI_FILE_CONTENT_ONLY_FULL_LINE); + PsiData psiData = mPsiExtractor.getPsiData(PsiData.ResourceType.IO); + assertEquals(psiData.getResourceType(), PsiData.ResourceType.IO); + assertEquals(psiData.getSomeAvg10SecPercentage(), (float) -1.0); + assertEquals(psiData.getSomeAvg60SecPercentage(), (float) -1.0); + assertEquals(psiData.getSomeAvg300SecPercentage(), (float) -1.0); + assertEquals(psiData.getSomeTotalUsec(), -1); + assertEquals(psiData.getFullAvg10SecPercentage(), (float) 0.21); + assertEquals(psiData.getFullAvg60SecPercentage(), (float) 0.43); + assertEquals(psiData.getFullAvg300SecPercentage(), (float) 0.65); + assertEquals(psiData.getFullTotalUsec(), 87654321); + } + + @Test + public void getPsiData_emptyFile() { + Mockito.when(mPsiReader.read("/proc/pressure/memory")).thenReturn(""); + PsiData psiData = mPsiExtractor.getPsiData(PsiData.ResourceType.MEMORY); + assertEquals(psiData, null); + + Mockito.when(mPsiReader.read("/proc/pressure/cpu")).thenReturn(""); + psiData = mPsiExtractor.getPsiData(PsiData.ResourceType.CPU); + assertEquals(psiData, null); + + Mockito.when(mPsiReader.read("/proc/pressure/io")).thenReturn(""); + psiData = mPsiExtractor.getPsiData(PsiData.ResourceType.IO); + assertEquals(psiData, null); + } + + @Test + public void getPsiData_avg60Missing() { + Mockito.when(mPsiReader.read("/proc/pressure/memory")).thenReturn( + BOTH_AVG60_MISSING_PSI_FILE_CONTENT); + PsiData psiData = mPsiExtractor.getPsiData(PsiData.ResourceType.MEMORY); + assertEquals(psiData, null); + + Mockito.when(mPsiReader.read("/proc/pressure/cpu")).thenReturn( + BOTH_AVG60_MISSING_PSI_FILE_CONTENT); + psiData = mPsiExtractor.getPsiData(PsiData.ResourceType.CPU); + assertEquals(psiData, null); + + Mockito.when(mPsiReader.read("/proc/pressure/io")).thenReturn( + BOTH_AVG60_MISSING_PSI_FILE_CONTENT); + psiData = mPsiExtractor.getPsiData(PsiData.ResourceType.IO); + assertEquals(psiData, null); + } + + @Test + public void getPsiData_totalMissing() { + Mockito.when(mPsiReader.read("/proc/pressure/memory")).thenReturn( + BOTH_TOTAL_MISSING_PSI_FILE_CONTENT); + PsiData psiData = mPsiExtractor.getPsiData(PsiData.ResourceType.MEMORY); + assertEquals(psiData, null); + + Mockito.when(mPsiReader.read("/proc/pressure/cpu")).thenReturn( + BOTH_TOTAL_MISSING_PSI_FILE_CONTENT); + psiData = mPsiExtractor.getPsiData(PsiData.ResourceType.CPU); + assertEquals(psiData, null); + + Mockito.when(mPsiReader.read("/proc/pressure/io")).thenReturn( + BOTH_TOTAL_MISSING_PSI_FILE_CONTENT); + psiData = mPsiExtractor.getPsiData(PsiData.ResourceType.IO); + assertEquals(psiData, null); + } + + @Test + public void getPsiData_avg10NonNum() { + Mockito.when(mPsiReader.read("/proc/pressure/memory")).thenReturn( + NON_NUM_AVG10_PSI_FILE_CONTENT); + PsiData psiData = mPsiExtractor.getPsiData(PsiData.ResourceType.MEMORY); + assertEquals(psiData, null); + + Mockito.when(mPsiReader.read("/proc/pressure/cpu")).thenReturn( + NON_NUM_AVG10_PSI_FILE_CONTENT); + psiData = mPsiExtractor.getPsiData(PsiData.ResourceType.CPU); + assertEquals(psiData, null); + + Mockito.when(mPsiReader.read("/proc/pressure/io")).thenReturn( + NON_NUM_AVG10_PSI_FILE_CONTENT); + psiData = mPsiExtractor.getPsiData(PsiData.ResourceType.IO); + assertEquals(psiData, null); + } + + @Test + public void getPsiData_avg300NonNum() { + Mockito.when(mPsiReader.read("/proc/pressure/memory")).thenReturn( + NON_NUM_AVG300_PSI_FILE_CONTENT); + PsiData psiData = mPsiExtractor.getPsiData(PsiData.ResourceType.MEMORY); + assertEquals(psiData, null); + + Mockito.when(mPsiReader.read("/proc/pressure/cpu")).thenReturn( + NON_NUM_AVG300_PSI_FILE_CONTENT); + psiData = mPsiExtractor.getPsiData(PsiData.ResourceType.CPU); + assertEquals(psiData, null); + + Mockito.when(mPsiReader.read("/proc/pressure/io")).thenReturn( + NON_NUM_AVG300_PSI_FILE_CONTENT); + psiData = mPsiExtractor.getPsiData(PsiData.ResourceType.IO); + assertEquals(psiData, null); + } +} diff --git a/services/tests/servicestests/src/com/android/server/supervision/SupervisionServiceTest.kt b/services/tests/servicestests/src/com/android/server/supervision/SupervisionServiceTest.kt index 6bd4279152ab..79b0623640f6 100644 --- a/services/tests/servicestests/src/com/android/server/supervision/SupervisionServiceTest.kt +++ b/services/tests/servicestests/src/com/android/server/supervision/SupervisionServiceTest.kt @@ -16,12 +16,20 @@ package com.android.server.supervision +import android.app.admin.DevicePolicyManagerInternal +import android.content.ComponentName +import android.content.Context +import android.content.pm.UserInfo import android.os.Bundle +import android.platform.test.annotations.RequiresFlagsEnabled +import android.platform.test.flag.junit.DeviceFlagsValueProvider import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import com.android.internal.R import com.android.server.LocalServices +import com.android.server.SystemService.TargetUser import com.android.server.pm.UserManagerInternal import com.google.common.truth.Truth.assertThat -import androidx.test.platform.app.InstrumentationRegistry import org.junit.Before import org.junit.Rule import org.junit.Test @@ -29,11 +37,12 @@ import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.junit.MockitoJUnit import org.mockito.junit.MockitoRule +import org.mockito.kotlin.whenever /** - * Unit tests for {@link SupervisionService}. - * <p/> - * Run with <code>atest SupervisionServiceTest</code>. + * Unit tests for [SupervisionService]. + * + * Run with `atest SupervisionServiceTest`. */ @RunWith(AndroidJUnit4::class) class SupervisionServiceTest { @@ -41,18 +50,21 @@ class SupervisionServiceTest { const val USER_ID = 100 } - private lateinit var service: SupervisionService + @get:Rule val mocks: MockitoRule = MockitoJUnit.rule() + @get:Rule val checkFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule() - @Rule - @JvmField - val mocks: MockitoRule = MockitoJUnit.rule() + @Mock private lateinit var mockDpmInternal: DevicePolicyManagerInternal + @Mock private lateinit var mockUserManagerInternal: UserManagerInternal - @Mock - private lateinit var mockUserManagerInternal: UserManagerInternal + private lateinit var context: Context + private lateinit var service: SupervisionService @Before - fun setup() { - val context = InstrumentationRegistry.getInstrumentation().context + fun setUp() { + context = InstrumentationRegistry.getInstrumentation().context + + LocalServices.removeServiceForTest(DevicePolicyManagerInternal::class.java) + LocalServices.addService(DevicePolicyManagerInternal::class.java, mockDpmInternal) LocalServices.removeServiceForTest(UserManagerInternal::class.java) LocalServices.addService(UserManagerInternal::class.java, mockUserManagerInternal) @@ -61,7 +73,46 @@ class SupervisionServiceTest { } @Test - fun testSetSupervisionEnabledForUser() { + @RequiresFlagsEnabled(android.app.admin.flags.Flags.FLAG_ENABLE_SUPERVISION_SERVICE_SYNC) + fun syncStateWithDevicePolicyManager_supervisionAppIsProfileOwner_enablesSupervision() { + val supervisionPackageName = + context.getResources().getString(R.string.config_systemSupervision) + + whenever(mockDpmInternal.getProfileOwnerAsUser(USER_ID)) + .thenReturn(ComponentName(supervisionPackageName, "MainActivity")) + + service.syncStateWithDevicePolicyManager(newTargetUser(USER_ID)) + + assertThat(service.isSupervisionEnabledForUser(USER_ID)).isTrue() + } + + @Test + @RequiresFlagsEnabled(android.app.admin.flags.Flags.FLAG_ENABLE_SUPERVISION_SERVICE_SYNC) + fun syncStateWithDevicePolicyManager_userPreCreated_doesNotEnableSupervision() { + val supervisionPackageName = + context.getResources().getString(R.string.config_systemSupervision) + + whenever(mockDpmInternal.getProfileOwnerAsUser(USER_ID)) + .thenReturn(ComponentName(supervisionPackageName, "MainActivity")) + + service.syncStateWithDevicePolicyManager(newTargetUser(USER_ID, preCreated = true)) + + assertThat(service.isSupervisionEnabledForUser(USER_ID)).isFalse() + } + + @Test + @RequiresFlagsEnabled(android.app.admin.flags.Flags.FLAG_ENABLE_SUPERVISION_SERVICE_SYNC) + fun syncStateWithDevicePolicyManager_supervisionAppIsNotProfileOwner_doesNotEnableSupervision() { + whenever(mockDpmInternal.getProfileOwnerAsUser(USER_ID)) + .thenReturn(ComponentName("other.package", "MainActivity")) + + service.syncStateWithDevicePolicyManager(newTargetUser(USER_ID)) + + assertThat(service.isSupervisionEnabledForUser(USER_ID)).isFalse() + } + + @Test + fun setSupervisionEnabledForUser() { assertThat(service.isSupervisionEnabledForUser(USER_ID)).isFalse() service.setSupervisionEnabledForUser(USER_ID, true) @@ -72,7 +123,18 @@ class SupervisionServiceTest { } @Test - fun testSetSupervisionLockscreenEnabledForUser() { + fun supervisionEnabledForUser_internal() { + assertThat(service.isSupervisionEnabledForUser(USER_ID)).isFalse() + + service.mInternal.setSupervisionEnabledForUser(USER_ID, true) + assertThat(service.isSupervisionEnabledForUser(USER_ID)).isTrue() + + service.mInternal.setSupervisionEnabledForUser(USER_ID, false) + assertThat(service.isSupervisionEnabledForUser(USER_ID)).isFalse() + } + + @Test + fun setSupervisionLockscreenEnabledForUser() { var userData = service.getUserDataLocked(USER_ID) assertThat(userData.supervisionLockScreenEnabled).isFalse() assertThat(userData.supervisionLockScreenOptions).isNull() @@ -87,4 +149,10 @@ class SupervisionServiceTest { assertThat(userData.supervisionLockScreenEnabled).isFalse() assertThat(userData.supervisionLockScreenOptions).isNull() } + + private fun newTargetUser(userId: Int, preCreated: Boolean = false): TargetUser { + val userInfo = UserInfo(userId, /* name= */ "tempUser", /* flags= */ 0) + userInfo.preCreated = preCreated + return TargetUser(userInfo) + } } diff --git a/services/tests/uiservicestests/src/com/android/server/UiServiceTestCase.java b/services/tests/uiservicestests/src/com/android/server/UiServiceTestCase.java index c9d5241c57b7..b3ec2153542a 100644 --- a/services/tests/uiservicestests/src/com/android/server/UiServiceTestCase.java +++ b/services/tests/uiservicestests/src/com/android/server/UiServiceTestCase.java @@ -30,7 +30,6 @@ import android.testing.TestableContext; import androidx.test.InstrumentationRegistry; -import com.android.server.pm.UserManagerInternal; import com.android.server.uri.UriGrantsManagerInternal; import org.junit.After; @@ -42,7 +41,6 @@ import org.mockito.MockitoAnnotations; public class UiServiceTestCase { @Mock protected PackageManagerInternal mPmi; - @Mock protected UserManagerInternal mUmi; @Mock protected UriGrantsManagerInternal mUgmInternal; protected static final String PKG_N_MR1 = "com.example.n_mr1"; @@ -94,8 +92,6 @@ public class UiServiceTestCase { } }); - LocalServices.removeServiceForTest(UserManagerInternal.class); - LocalServices.addService(UserManagerInternal.class, mUmi); LocalServices.removeServiceForTest(UriGrantsManagerInternal.class); LocalServices.addService(UriGrantsManagerInternal.class, mUgmInternal); when(mUgmInternal.checkGrantUriPermission( diff --git a/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java index 0b89c11a11f4..cc0286508cdc 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java @@ -2291,7 +2291,9 @@ public class GroupHelperTest extends UiServiceTestCase { } @Test - @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING, FLAG_NOTIFICATION_REGROUP_ON_CLASSIFICATION}) + @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING, + FLAG_NOTIFICATION_CLASSIFICATION, + FLAG_NOTIFICATION_REGROUP_ON_CLASSIFICATION}) public void testMoveAggregateGroups_updateChannel_multipleChannels_regroupOnClassifEnabled() { final String pkg = "package"; final String expectedGroupKey_alerting = GroupHelper.getFullAggregateGroupKey(pkg, @@ -2366,7 +2368,9 @@ public class GroupHelperTest extends UiServiceTestCase { } @Test - @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING, FLAG_NOTIFICATION_REGROUP_ON_CLASSIFICATION}) + @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING, + FLAG_NOTIFICATION_CLASSIFICATION, + FLAG_NOTIFICATION_REGROUP_ON_CLASSIFICATION}) public void testMoveSections_notificationBundled() { final List<NotificationRecord> notificationList = new ArrayList<>(); final String pkg = "package"; @@ -2406,11 +2410,13 @@ public class GroupHelperTest extends UiServiceTestCase { NotificationChannel.NEWS_ID); for (NotificationRecord record: notificationList) { if (record.getChannel().getId().equals(channel1.getId()) + && record.getNotification().isGroupChild() && record.getSbn().getId() % 2 == 0) { record.updateNotificationChannel(socialChannel); mGroupHelper.onChannelUpdated(record); } if (record.getChannel().getId().equals(channel1.getId()) + && record.getNotification().isGroupChild() && record.getSbn().getId() % 2 != 0) { record.updateNotificationChannel(newsChannel); mGroupHelper.onChannelUpdated(record); @@ -2436,7 +2442,9 @@ public class GroupHelperTest extends UiServiceTestCase { } @Test - @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING, FLAG_NOTIFICATION_REGROUP_ON_CLASSIFICATION}) + @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING, + FLAG_NOTIFICATION_CLASSIFICATION, + FLAG_NOTIFICATION_REGROUP_ON_CLASSIFICATION}) public void testCacheAndCancelAppSummary_notificationBundled() { // check that the original app summary is canceled & cached on classification regrouping final List<NotificationRecord> notificationList = new ArrayList<>(); @@ -2468,7 +2476,8 @@ public class GroupHelperTest extends UiServiceTestCase { NotificationChannel.SOCIAL_MEDIA_ID, NotificationChannel.SOCIAL_MEDIA_ID, IMPORTANCE_DEFAULT); for (NotificationRecord record: notificationList) { - if (record.getChannel().getId().equals(channel1.getId())) { + if (record.getChannel().getId().equals(channel1.getId()) + && record.getNotification().isGroupChild()) { record.updateNotificationChannel(socialChannel); mGroupHelper.onChannelUpdated(record); } @@ -2495,6 +2504,7 @@ public class GroupHelperTest extends UiServiceTestCase { @Test @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING, + FLAG_NOTIFICATION_CLASSIFICATION, FLAG_NOTIFICATION_REGROUP_ON_CLASSIFICATION, FLAG_NOTIFICATION_FORCE_GROUP_SINGLETONS}) public void testSingletonGroupsRegrouped_notificationBundledBeforeDelayTimeout() { @@ -2525,7 +2535,8 @@ public class GroupHelperTest extends UiServiceTestCase { BASE_FLAGS, mSmallIcon, COLOR_DEFAULT, DEFAULT_VISIBILITY, DEFAULT_GROUP_ALERT, NotificationChannel.SOCIAL_MEDIA_ID); for (NotificationRecord record: notificationList) { - if (record.getOriginalGroupKey().contains("testGrp")) { + if (record.getOriginalGroupKey().contains("testGrp") + && record.getNotification().isGroupChild()) { record.updateNotificationChannel(socialChannel); mGroupHelper.onChannelUpdated(record); } @@ -2569,6 +2580,7 @@ public class GroupHelperTest extends UiServiceTestCase { @Test @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING, + FLAG_NOTIFICATION_CLASSIFICATION, FLAG_NOTIFICATION_REGROUP_ON_CLASSIFICATION, FLAG_NOTIFICATION_FORCE_GROUP_SINGLETONS}) public void testSingletonGroupsRegrouped_notificationBundledAfterDelayTimeout() { @@ -2623,7 +2635,8 @@ public class GroupHelperTest extends UiServiceTestCase { BASE_FLAGS, mSmallIcon, COLOR_DEFAULT, DEFAULT_VISIBILITY, DEFAULT_GROUP_ALERT, NotificationChannel.SOCIAL_MEDIA_ID); for (NotificationRecord record: notificationList) { - if (record.getOriginalGroupKey().contains("testGrp")) { + if (record.getOriginalGroupKey().contains("testGrp") + && record.getNotification().isGroupChild()) { record.updateNotificationChannel(socialChannel); mGroupHelper.onChannelUpdated(record); } @@ -2642,6 +2655,64 @@ public class GroupHelperTest extends UiServiceTestCase { } @Test + @EnableFlags({FLAG_NOTIFICATION_FORCE_GROUPING, + FLAG_NOTIFICATION_REGROUP_ON_CLASSIFICATION, + FLAG_NOTIFICATION_CLASSIFICATION}) + public void testValidGroupsRegrouped_notificationBundledWhileEnqueued() { + // Check that valid group notifications are regrouped if classification is done + // before onNotificationPostedWithDelay (within DELAY_FOR_ASSISTANT_TIME) + final List<NotificationRecord> notificationList = new ArrayList<>(); + final ArrayMap<String, NotificationRecord> summaryByGroup = new ArrayMap<>(); + final String pkg = "package"; + + final int summaryId = 0; + final int numChildren = 3; + // Post a regular/valid group: summary + notifications + NotificationRecord summary = getNotificationRecord(pkg, summaryId, + String.valueOf(summaryId), UserHandle.SYSTEM, "testGrp", true); + notificationList.add(summary); + summaryByGroup.put(summary.getGroupKey(), summary); + for (int i = 0; i < numChildren; i++) { + NotificationRecord child = getNotificationRecord(pkg, i + 42, String.valueOf(i + 42), + UserHandle.SYSTEM, "testGrp", false); + notificationList.add(child); + } + + // Classify/bundle child notifications. Don't call onChannelUpdated, + // adjustments applied while enqueued will use NotificationAdjustmentExtractor. + final NotificationChannel socialChannel = new NotificationChannel( + NotificationChannel.SOCIAL_MEDIA_ID, NotificationChannel.SOCIAL_MEDIA_ID, + IMPORTANCE_DEFAULT); + final String expectedGroupKey_social = GroupHelper.getFullAggregateGroupKey(pkg, + AGGREGATE_GROUP_KEY + "SocialSection", UserHandle.SYSTEM.getIdentifier()); + final NotificationAttributes expectedSummaryAttr_social = new NotificationAttributes( + BASE_FLAGS, mSmallIcon, COLOR_DEFAULT, DEFAULT_VISIBILITY, DEFAULT_GROUP_ALERT, + NotificationChannel.SOCIAL_MEDIA_ID); + for (NotificationRecord record: notificationList) { + if (record.getOriginalGroupKey().contains("testGrp") + && record.getNotification().isGroupChild()) { + record.updateNotificationChannel(socialChannel); + } + } + + // Check that notifications are forced grouped and app-provided summaries are canceled + for (NotificationRecord record: notificationList) { + mGroupHelper.onNotificationPostedWithDelay(record, notificationList, summaryByGroup); + } + + verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(), + eq(expectedGroupKey_social), anyInt(), eq(expectedSummaryAttr_social)); + verify(mCallback, times(numChildren)).addAutoGroup(anyString(), eq(expectedGroupKey_social), + eq(true)); + verify(mCallback, never()).removeAutoGroup(anyString()); + verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString(), anyString()); + verify(mCallback, times(numChildren - 1)).updateAutogroupSummary(anyInt(), anyString(), + anyString(), any()); + verify(mCallback, times(numChildren)).removeAppProvidedSummaryOnClassification(anyString(), + anyString()); + } + + @Test @EnableFlags(FLAG_NOTIFICATION_FORCE_GROUPING) public void testMoveAggregateGroups_updateChannel_groupsUngrouped() { final String pkg = "package"; diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java index 48bc9d7c51a1..e5c42082ab97 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java @@ -1484,7 +1484,6 @@ public class ManagedServicesTest extends UiServiceTestCase { assertTrue(componentsToUnbind.get(0).contains(ComponentName.unflattenFromString("c/c"))); } - @SuppressWarnings("GuardedBy") @Test public void populateComponentsToBind() { ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles, mIpm, @@ -1508,8 +1507,7 @@ public class ManagedServicesTest extends UiServiceTestCase { SparseArray<Set<ComponentName>> componentsToBind = new SparseArray<>(); - service.populateComponentsToBind(componentsToBind, users, approvedComponentsByUser, - /* isVisibleBackgroundUser= */ false); + service.populateComponentsToBind(componentsToBind, users, approvedComponentsByUser); assertEquals(2, componentsToBind.size()); assertEquals(1, componentsToBind.get(0).size()); @@ -1519,33 +1517,6 @@ public class ManagedServicesTest extends UiServiceTestCase { assertTrue(componentsToBind.get(10).contains(ComponentName.unflattenFromString("c/c"))); } - @SuppressWarnings("GuardedBy") - @Test - public void populateComponentsToBind_isVisibleBackgroundUser_addComponentsToBindButNotAddToEnabledComponent() { - ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles, mIpm, - APPROVAL_BY_COMPONENT); - - SparseArray<ArraySet<ComponentName>> approvedComponentsByUser = new SparseArray<>(); - ArraySet<ComponentName> allowed = new ArraySet<>(); - allowed.add(ComponentName.unflattenFromString("pkg1/cmp1")); - approvedComponentsByUser.put(11, allowed); - IntArray users = new IntArray(); - users.add(11); - - SparseArray<Set<ComponentName>> componentsToBind = new SparseArray<>(); - - service.populateComponentsToBind(componentsToBind, users, approvedComponentsByUser, - /* isVisibleBackgroundUser= */ true); - - assertEquals(1, componentsToBind.size()); - assertEquals(1, componentsToBind.get(11).size()); - assertTrue(componentsToBind.get(11).contains(ComponentName.unflattenFromString( - "pkg1/cmp1"))); - assertThat(service.isComponentEnabledForCurrentProfiles( - new ComponentName("pkg1", "cmp1"))).isFalse(); - assertThat(service.isComponentEnabledForPackage("pkg1")).isFalse(); - } - @Test public void testOnNullBinding() throws Exception { Context context = mock(Context.class); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index eae587bc0187..704c1b858b8d 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -210,6 +210,7 @@ import android.app.Person; import android.app.RemoteInput; import android.app.RemoteInputHistoryItem; import android.app.StatsManager; +import android.app.ZenBypassingApp; import android.app.admin.DevicePolicyManagerInternal; import android.app.backup.BackupRestoreEventLogger; import android.app.job.JobScheduler; @@ -360,6 +361,9 @@ import org.mockito.MockitoAnnotations; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; +import platform.test.runner.parameterized.ParameterizedAndroidJunit4; +import platform.test.runner.parameterized.Parameters; + import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; @@ -374,9 +378,6 @@ import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.function.Consumer; -import platform.test.runner.parameterized.ParameterizedAndroidJunit4; -import platform.test.runner.parameterized.Parameters; - @SmallTest @RunWith(ParameterizedAndroidJunit4.class) @RunWithLooper @@ -489,7 +490,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { private final NotificationChannel mParentChannel = new NotificationChannel(PARENT_CHANNEL_ID, "parentName", IMPORTANCE_DEFAULT); private final NotificationChannel mConversationChannel = - new NotificationChannel(CONVERSATION_CHANNEL_ID, "conversationName", IMPORTANCE_DEFAULT); + new NotificationChannel( + CONVERSATION_CHANNEL_ID, "conversationName", IMPORTANCE_DEFAULT); private static final String PARENT_CHANNEL_ID = "parentChannelId"; private static final String CONVERSATION_CHANNEL_ID = "conversationChannelId"; @@ -4296,8 +4298,13 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { new NotificationChannel("foo", "foo", IMPORTANCE_HIGH)); Notification.TvExtender tv = new Notification.TvExtender().setChannelId("foo"); - mBinderService.enqueueNotificationWithTag(mPkg, mPkg, "testTvExtenderChannelOverride_onTv", 0, - generateNotificationRecord(null, tv).getNotification(), mUserId); + mBinderService.enqueueNotificationWithTag( + mPkg, + mPkg, + "testTvExtenderChannelOverride_onTv", + 0, + generateNotificationRecord(null, tv).getNotification(), + mUserId); verify(mPreferencesHelper, times(1)).getConversationNotificationChannel( anyString(), anyInt(), eq("foo"), eq(null), anyBoolean(), anyBoolean()); } @@ -4311,8 +4318,13 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mTestNotificationChannel); Notification.TvExtender tv = new Notification.TvExtender().setChannelId("foo"); - mBinderService.enqueueNotificationWithTag(mPkg, mPkg, "testTvExtenderChannelOverride_notOnTv", - 0, generateNotificationRecord(null, tv).getNotification(), mUserId); + mBinderService.enqueueNotificationWithTag( + mPkg, + mPkg, + "testTvExtenderChannelOverride_notOnTv", + 0, + generateNotificationRecord(null, tv).getNotification(), + mUserId); verify(mPreferencesHelper, times(1)).getConversationNotificationChannel( anyString(), anyInt(), eq(mTestNotificationChannel.getId()), eq(null), anyBoolean(), anyBoolean()); @@ -7745,9 +7757,21 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { .setContentTitle("foo") .setSmallIcon(android.R.drawable.sym_def_app_icon) .setStyle(new Notification.MessagingStyle("").addMessage(message2)); - NotificationRecord recordB = new NotificationRecord(mContext, new StatusBarNotification(mPkg, - mPkg, 0, "tag", mUid, 0, nbB.build(), UserHandle.getUserHandleForUid(mUid), null, 0), - c); + NotificationRecord recordB = + new NotificationRecord( + mContext, + new StatusBarNotification( + mPkg, + mPkg, + 0, + "tag", + mUid, + 0, + nbB.build(), + UserHandle.getUserHandleForUid(mUid), + null, + 0), + c); // Update means we drop access to first reset(mUgmInternal); @@ -13174,6 +13198,37 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test + public void getPackagesBypassingDnd_blocked() + throws RemoteException, PackageManager.NameNotFoundException { + + NotificationChannel channel1 = new NotificationChannel("id1", "name1", + NotificationManager.IMPORTANCE_MAX); + NotificationChannel channel2 = new NotificationChannel("id3", "name3", + NotificationManager.IMPORTANCE_MAX); + NotificationChannel channel3 = new NotificationChannel("id4", "name3", + NotificationManager.IMPORTANCE_MAX); + channel1.setBypassDnd(true); + channel2.setBypassDnd(true); + channel3.setBypassDnd(false); + // has DND access, so can set bypassDnd attribute + mService.mPreferencesHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true, + /*has DND access*/ true, UID_N_MR1, false); + mService.mPreferencesHelper.createNotificationChannel(PKG_P, UID_P, channel2, true, true, + UID_P, false); + mService.mPreferencesHelper.createNotificationChannel(PKG_P, UID_P, channel3, true, true, + UID_P, false); + + when(mPackageManager.getPackageUid(eq(PKG_P), anyLong(), anyInt())).thenReturn(UID_P); + when(mPackageManager.getPackageUid(eq(PKG_N_MR1), anyLong(), anyInt())) + .thenReturn(UID_N_MR1); + when(mPermissionHelper.hasPermission(UID_N_MR1)).thenReturn(false); + when(mPermissionHelper.hasPermission(UID_P)).thenReturn(true); + + assertThat(mBinderService.getPackagesBypassingDnd(UserHandle.getUserId(UID_P)).getList()) + .containsExactly(new ZenBypassingApp(PKG_P, false)); + } + + @Test public void testGetNotificationChannelsBypassingDnd_blocked() throws RemoteException { mService.setPreferencesHelper(mPreferencesHelper); @@ -13187,125 +13242,11 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { @Test public void testGetPackagesBypassingDnd_empty() throws RemoteException { mService.setPreferencesHelper(mPreferencesHelper); - List<String> result = mBinderService.getPackagesBypassingDnd(mUserId, true); + List<String> result = mBinderService.getPackagesBypassingDnd(mUserId).getList(); assertThat(result).isEmpty(); } @Test - public void testGetPackagesBypassingDnd_excludeConversationChannels() throws RemoteException { - mService.setPreferencesHelper(mPreferencesHelper); - - // Set packages - PackageInfo pkg0 = new PackageInfo(); - pkg0.packageName = "pkg0"; - pkg0.applicationInfo = new ApplicationInfo(); - pkg0.applicationInfo.uid = mUid; - PackageInfo pkg1 = new PackageInfo(); - pkg1.packageName = "pkg1"; - pkg1.applicationInfo = new ApplicationInfo(); - pkg1.applicationInfo.uid = mUid; - PackageInfo pkg2 = new PackageInfo(); - pkg2.packageName = "pkg2"; - pkg2.applicationInfo = new ApplicationInfo(); - pkg2.applicationInfo.uid = mUid; - - when(mPackageManagerClient.getInstalledPackagesAsUser(0, mUserId)) - .thenReturn(List.of(pkg0, pkg1, pkg2)); - - // Conversation channels - NotificationChannel nc0 = new NotificationChannel("id0", "id0", - NotificationManager.IMPORTANCE_HIGH); - nc0.setConversationId("parentChannel", "conversationId"); - - // Demoted conversation channel - NotificationChannel nc1 = new NotificationChannel("id1", "id1", - NotificationManager.IMPORTANCE_HIGH); - nc1.setConversationId("parentChannel", "conversationId"); - nc1.setDemoted(true); - - // Non-conversation channels - NotificationChannel nc2 = new NotificationChannel("id2", "id2", - NotificationManager.IMPORTANCE_HIGH); - NotificationChannel nc3 = new NotificationChannel("id3", "id3", - NotificationManager.IMPORTANCE_HIGH); - - ParceledListSlice<NotificationChannel> pls0 = - new ParceledListSlice(ImmutableList.of(nc0)); - ParceledListSlice<NotificationChannel> pls1 = - new ParceledListSlice(ImmutableList.of(nc1)); - ParceledListSlice<NotificationChannel> pls2 = - new ParceledListSlice(ImmutableList.of(nc2, nc3)); - - when(mPreferencesHelper.getNotificationChannelsBypassingDnd("pkg0", mUid)) - .thenReturn(pls0); - when(mPreferencesHelper.getNotificationChannelsBypassingDnd("pkg1", mUid)) - .thenReturn(pls1); - when(mPreferencesHelper.getNotificationChannelsBypassingDnd("pkg2", mUid)) - .thenReturn(pls2); - - List<String> result = mBinderService.getPackagesBypassingDnd(mUserId, false); - - assertThat(result).containsExactly("pkg1", "pkg2"); - } - - @Test - public void testGetPackagesBypassingDnd_includeConversationChannels() throws RemoteException { - mService.setPreferencesHelper(mPreferencesHelper); - - // Set packages - PackageInfo pkg0 = new PackageInfo(); - pkg0.packageName = "pkg0"; - pkg0.applicationInfo = new ApplicationInfo(); - pkg0.applicationInfo.uid = mUid; - PackageInfo pkg1 = new PackageInfo(); - pkg1.packageName = "pkg1"; - pkg1.applicationInfo = new ApplicationInfo(); - pkg1.applicationInfo.uid = mUid; - PackageInfo pkg2 = new PackageInfo(); - pkg2.packageName = "pkg2"; - pkg2.applicationInfo = new ApplicationInfo(); - pkg2.applicationInfo.uid = mUid; - - when(mPackageManagerClient.getInstalledPackagesAsUser(0, mUserId)) - .thenReturn(List.of(pkg0, pkg1, pkg2)); - - // Conversation channels - NotificationChannel nc0 = new NotificationChannel("id0", "id0", - NotificationManager.IMPORTANCE_HIGH); - nc0.setConversationId("parentChannel", "conversationId"); - - // Demoted conversation channel - NotificationChannel nc1 = new NotificationChannel("id1", "id1", - NotificationManager.IMPORTANCE_HIGH); - nc1.setConversationId("parentChannel", "conversationId"); - nc1.setDemoted(true); - - // Non-conversation channels - NotificationChannel nc2 = new NotificationChannel("id2", "id2", - NotificationManager.IMPORTANCE_HIGH); - NotificationChannel nc3 = new NotificationChannel("id3", "id3", - NotificationManager.IMPORTANCE_HIGH); - - ParceledListSlice<NotificationChannel> pls0 = - new ParceledListSlice(ImmutableList.of(nc0)); - ParceledListSlice<NotificationChannel> pls1 = - new ParceledListSlice(ImmutableList.of(nc1)); - ParceledListSlice<NotificationChannel> pls2 = - new ParceledListSlice(ImmutableList.of(nc2, nc3)); - - when(mPreferencesHelper.getNotificationChannelsBypassingDnd("pkg0", mUid)) - .thenReturn(pls0); - when(mPreferencesHelper.getNotificationChannelsBypassingDnd("pkg1", mUid)) - .thenReturn(pls1); - when(mPreferencesHelper.getNotificationChannelsBypassingDnd("pkg2", mUid)) - .thenReturn(pls2); - - List<String> result = mBinderService.getPackagesBypassingDnd(mUserId, true); - - assertThat(result).containsExactly("pkg0", "pkg1", "pkg2"); - } - - @Test public void testMatchesCallFilter_noPermissionShouldThrow() throws Exception { // set the testable NMS to not system uid/appid mService.isSystemUid = false; @@ -15482,8 +15423,13 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { for (int i = 0; i < NotificationManagerService.MAX_PACKAGE_NOTIFICATIONS; i++) { StatusBarNotification sbn = generateNotificationRecord(mTestNotificationChannel, i, null, false).getSbn(); - mBinderService.enqueueNotificationWithTag(mPkg, mPkg, "testCannotPostNonUijWhenOverLimit", - sbn.getId(), sbn.getNotification(), sbn.getUserId()); + mBinderService.enqueueNotificationWithTag( + mPkg, + mPkg, + "testCannotPostNonUijWhenOverLimit", + sbn.getId(), + sbn.getNotification(), + sbn.getUserId()); waitForIdle(); } @@ -16213,6 +16159,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { initNMS(SystemService.PHASE_SYSTEM_SERVICES_READY); mInternalService.setDeviceEffectsApplier(mock(DeviceEffectsApplier.class)); + + mService.onBootPhase(SystemService.PHASE_THIRD_PARTY_APPS_CAN_START, mMainLooper); // No exception! } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationVisitUrisTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationVisitUrisTest.java index 411a6102f45a..361df94e8a90 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationVisitUrisTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationVisitUrisTest.java @@ -44,7 +44,7 @@ import android.widget.RemoteViews; import androidx.annotation.NonNull; import androidx.test.InstrumentationRegistry; -import androidx.test.runner.AndroidJUnit4; +import androidx.test.ext.junit.runners.AndroidJUnit4; import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags; import com.android.server.UiServiceTestCase; @@ -89,7 +89,7 @@ import java.util.stream.Stream; import javax.annotation.Nullable; @RunWith(AndroidJUnit4.class) -@EnableFlags({Flags.FLAG_VISIT_PERSON_URI, Flags.FLAG_API_RICH_ONGOING}) +@EnableFlags({Flags.FLAG_API_RICH_ONGOING}) public class NotificationVisitUrisTest extends UiServiceTestCase { @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java index e1b478cd1a1b..dda060d5d586 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java @@ -108,6 +108,7 @@ import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationChannelGroup; import android.app.NotificationManager; +import android.app.ZenBypassingApp; import android.content.AttributionSource; import android.content.ContentProvider; import android.content.ContentResolver; @@ -2620,6 +2621,72 @@ public class PreferencesHelperTest extends UiServiceTestCase { } @Test + public void getPackagesBypassingDnd_noChannelsBypassing() throws Exception { + assertThat(mHelper.getPackagesBypassingDnd(UserHandle.getUserId(UID_N_MR1))).isEmpty(); + } + + @Test + public void getPackagesBypassingDnd_oneChannelBypassing_deleted() { + NotificationChannel channel1 = new NotificationChannel("id1", "name1", + NotificationManager.IMPORTANCE_MAX); + channel1.setBypassDnd(true); + channel1.setDeleted(true); + // has DND access, so can set bypassDnd attribute + mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true, + /*has DND access*/ true, UID_N_MR1, false); + + assertThat(mHelper.getPackagesBypassingDnd(UserHandle.getUserId(UID_N_MR1))).isEmpty(); + } + + @Test + public void getPackagesBypassingDnd_oneChannelBypassing_groupBlocked() { + int uid = UID_N_MR1; + NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1"); + NotificationChannel channel1 = new NotificationChannel("id1", "name1", + NotificationManager.IMPORTANCE_MAX); + channel1.setBypassDnd(true); + channel1.setGroup(ncg.getId()); + mHelper.createNotificationChannelGroup(PKG_N_MR1, uid, ncg, /* fromTargetApp */ true, + uid, false); + mHelper.createNotificationChannel(PKG_N_MR1, uid, channel1, true, /*has DND access*/ true, + uid, false); + ncg.setBlocked(true); + + assertThat(mHelper.getPackagesBypassingDnd(UserHandle.getUserId(uid))).isEmpty(); + } + + @Test + public void getPackagesBypassingDnd_multipleApps() { + List<ZenBypassingApp> expected = ImmutableList.of( + new ZenBypassingApp(PKG_O, true), new ZenBypassingApp(PKG_P, false)); + + NotificationChannel channel1 = new NotificationChannel("id1", "name1", + NotificationManager.IMPORTANCE_MAX); + NotificationChannel channel2 = new NotificationChannel("id2", "name2", + NotificationManager.IMPORTANCE_MAX); + NotificationChannel channel3 = new NotificationChannel("id3", "name3", + NotificationManager.IMPORTANCE_MAX); + NotificationChannel channel4 = new NotificationChannel("id4", "name3", + NotificationManager.IMPORTANCE_MAX); + channel1.setBypassDnd(false); + channel2.setBypassDnd(true); + channel3.setBypassDnd(true); + channel4.setBypassDnd(false); + // has DND access, so can set bypassDnd attribute + mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true, + /*has DND access*/ true, UID_N_MR1, false); + mHelper.createNotificationChannel(PKG_O, UID_O, channel2, true, true, + UID_O, false); + mHelper.createNotificationChannel(PKG_P, UID_P, channel3, true, true, + UID_P, false); + mHelper.createNotificationChannel(PKG_P, UID_P, channel4, true, true, + UID_P, false); + + assertThat(mHelper.getPackagesBypassingDnd(UserHandle.getUserId(UID_O))) + .containsExactlyElementsIn(expected); + } + + @Test public void testCreateAndDeleteCanChannelsBypassDnd_localSettings() { int uid = UserManager.isHeadlessSystemUserMode() ? UID_HEADLESS : UID_N_MR1; when(mPermissionHelper.hasPermission(uid)).thenReturn(true); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java index 4b94e103b9f4..020670dc0f0a 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java @@ -49,6 +49,8 @@ import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_CONTACTS; import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_STARRED; import static android.app.NotificationManager.Policy.STATE_PRIORITY_CHANNELS_BLOCKED; import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_BADGE; +import static android.app.backup.NotificationLoggingConstants.DATA_TYPE_ZEN_CONFIG; +import static android.app.backup.NotificationLoggingConstants.DATA_TYPE_ZEN_RULES; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.os.Process.SYSTEM_UID; import static android.provider.Settings.Global.ZEN_MODE_ALARMS; @@ -84,8 +86,6 @@ import static com.android.os.dnd.DNDProtoEnums.PEOPLE_STARRED; import static com.android.os.dnd.DNDProtoEnums.ROOT_CONFIG; import static com.android.os.dnd.DNDProtoEnums.STATE_ALLOW; import static com.android.os.dnd.DNDProtoEnums.STATE_DISALLOW; -import static android.app.backup.NotificationLoggingConstants.DATA_TYPE_ZEN_CONFIG; -import static android.app.backup.NotificationLoggingConstants.DATA_TYPE_ZEN_RULES; import static com.android.server.notification.ZenModeEventLogger.ACTIVE_RULE_TYPE_MANUAL; import static com.android.server.notification.ZenModeHelper.RULE_LIMIT_PER_PACKAGE; @@ -102,6 +102,7 @@ import static junit.framework.TestCase.fail; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; @@ -200,6 +201,9 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.xmlpull.v1.XmlPullParserException; +import platform.test.runner.parameterized.ParameterizedAndroidJunit4; +import platform.test.runner.parameterized.Parameters; + import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; @@ -219,9 +223,6 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; -import platform.test.runner.parameterized.ParameterizedAndroidJunit4; -import platform.test.runner.parameterized.Parameters; - @SmallTest @SuppressLint("GuardedBy") // It's ok for this test to access guarded methods from the service. @RunWith(ParameterizedAndroidJunit4.class) @@ -5348,6 +5349,22 @@ public class ZenModeHelperTest extends UiServiceTestCase { mTestableLooper.processAllMessages(); verify(mDeviceEffectsApplier).apply(eq(effects), eq(ORIGIN_APP)); + assertTrue(mZenModeHelper.hasDeviceEffectsApplier()); + } + + @Test + public void testHasDeviceEffectsApplier_returnsFalseIfNotSet() { + assertFalse(mZenModeHelper.hasDeviceEffectsApplier()); + } + + @Test + @EnableFlags(FLAG_MODES_API) + public void testSettingDeviceEffects_throwsExceptionIfAlreadySet() { + mZenModeHelper.setDeviceEffectsApplier(mDeviceEffectsApplier); + + assertThrows( + IllegalStateException.class, + () -> mZenModeHelper.setDeviceEffectsApplier(mDeviceEffectsApplier)); } @Test 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 28ae271e20fc..cf5323e1f3a5 100644 --- a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java +++ b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java @@ -288,7 +288,6 @@ public class ModifierShortcutTests extends ShortcutKeyTestBase { * Sends a KEYCODE_SCREENSHOT and validates screenshot is taken if flag is enabled */ @Test - @EnableFlags(com.android.hardware.input.Flags.FLAG_EMOJI_AND_SCREENSHOT_KEYCODES_AVAILABLE) @DisableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER) public void testTakeScreenshot_flagEnabled() { sendKeyCombination(new int[]{KEYCODE_SCREENSHOT}, 0); @@ -296,17 +295,6 @@ public class ModifierShortcutTests extends ShortcutKeyTestBase { } /** - * Sends a KEYCODE_SCREENSHOT and validates screenshot is not taken if flag is disabled - */ - @Test - @DisableFlags({com.android.hardware.input.Flags.FLAG_EMOJI_AND_SCREENSHOT_KEYCODES_AVAILABLE, - com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER}) - public void testTakeScreenshot_flagDisabled() { - sendKeyCombination(new int[]{KEYCODE_SCREENSHOT}, 0); - mPhoneWindowManager.assertTakeScreenshotNotCalled(); - } - - /** * META+CTRL+BACKSPACE for taking a bugreport when the flag is enabled. */ @Test diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java index 6596ee935b4b..a51ce9951ab4 100644 --- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java +++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java @@ -699,8 +699,8 @@ class TestPhoneWindowManager { void assertPowerWakeUp() { mTestLooper.dispatchAll(); - verify(mWindowWakeUpPolicy) - .wakeUpFromKey(anyLong(), eq(KeyEvent.KEYCODE_POWER), anyBoolean()); + verify(mWindowWakeUpPolicy).wakeUpFromKey( + eq(DEFAULT_DISPLAY), anyLong(), eq(KeyEvent.KEYCODE_POWER), anyBoolean()); } void assertNoPowerSleep() { diff --git a/services/tests/wmtests/src/com/android/server/policy/WindowWakeUpPolicyTests.java b/services/tests/wmtests/src/com/android/server/policy/WindowWakeUpPolicyTests.java index 7322e5a3b681..3ca352cfa60d 100644 --- a/services/tests/wmtests/src/com/android/server/policy/WindowWakeUpPolicyTests.java +++ b/services/tests/wmtests/src/com/android/server/policy/WindowWakeUpPolicyTests.java @@ -22,6 +22,7 @@ import static android.os.PowerManager.WAKE_REASON_GESTURE; import static android.os.PowerManager.WAKE_REASON_POWER_BUTTON; import static android.os.PowerManager.WAKE_REASON_WAKE_KEY; import static android.os.PowerManager.WAKE_REASON_WAKE_MOTION; +import static android.view.Display.DEFAULT_DISPLAY; import static android.view.InputDevice.SOURCE_ROTARY_ENCODER; import static android.view.InputDevice.SOURCE_TOUCHSCREEN; import static android.view.KeyEvent.KEYCODE_HOME; @@ -35,6 +36,7 @@ import static com.android.internal.R.bool.config_allowTheaterModeWakeFromCameraL import static com.android.internal.R.bool.config_allowTheaterModeWakeFromLidSwitch; import static com.android.internal.R.bool.config_allowTheaterModeWakeFromGesture; import static com.android.server.policy.Flags.FLAG_SUPPORT_INPUT_WAKEUP_DELEGATE; +import static com.android.server.power.feature.flags.Flags.FLAG_PER_DISPLAY_WAKE_BY_TOUCH; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; @@ -43,6 +45,7 @@ import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; @@ -52,6 +55,8 @@ import android.content.Context; import android.content.ContextWrapper; import android.content.res.Resources; import android.os.PowerManager; +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.Display; @@ -125,6 +130,7 @@ public final class WindowWakeUpPolicyTests { } @Test + @DisableFlags({FLAG_PER_DISPLAY_WAKE_BY_TOUCH}) public void testMotionWakeUpDelegation_wakePowerManagerIfDelegateDoesNotHandleWake() { setTheaterModeEnabled(false); mSetFlagsRule.enableFlags(FLAG_SUPPORT_INPUT_WAKEUP_DELEGATE); @@ -136,7 +142,8 @@ public final class WindowWakeUpPolicyTests { // Verify the policy wake up call succeeds because of the call on the delegate, and not // because of a PowerManager wake up. - assertThat(mPolicy.wakeUpFromMotion(200, SOURCE_TOUCHSCREEN, true)).isTrue(); + assertThat(mPolicy.wakeUpFromMotion( + mDefaultDisplay.getDisplayId(), 200, SOURCE_TOUCHSCREEN, true)).isTrue(); verify(mInputWakeUpDelegate).wakeUpFromMotion(200, SOURCE_TOUCHSCREEN, true); verifyNoPowerManagerWakeUp(); @@ -144,12 +151,14 @@ public final class WindowWakeUpPolicyTests { // Verify the policy wake up call succeeds because of the PowerManager wake up, since the // delegate would not handle the wake up request. - assertThat(mPolicy.wakeUpFromMotion(300, SOURCE_ROTARY_ENCODER, false)).isTrue(); + assertThat(mPolicy.wakeUpFromMotion( + mDefaultDisplay.getDisplayId(), 300, SOURCE_ROTARY_ENCODER, false)).isTrue(); verify(mInputWakeUpDelegate).wakeUpFromMotion(300, SOURCE_ROTARY_ENCODER, false); verify(mPowerManager).wakeUp(300, WAKE_REASON_WAKE_MOTION, "android.policy:MOTION"); } @Test + @DisableFlags({FLAG_PER_DISPLAY_WAKE_BY_TOUCH}) public void testKeyWakeUpDelegation_wakePowerManagerIfDelegateDoesNotHandleWake() { setTheaterModeEnabled(false); mSetFlagsRule.enableFlags(FLAG_SUPPORT_INPUT_WAKEUP_DELEGATE); @@ -161,7 +170,7 @@ public final class WindowWakeUpPolicyTests { // Verify the policy wake up call succeeds because of the call on the delegate, and not // because of a PowerManager wake up. - assertThat(mPolicy.wakeUpFromKey(200, KEYCODE_POWER, true)).isTrue(); + assertThat(mPolicy.wakeUpFromKey(DEFAULT_DISPLAY, 200, KEYCODE_POWER, true)).isTrue(); verify(mInputWakeUpDelegate).wakeUpFromKey(200, KEYCODE_POWER, true); verifyNoPowerManagerWakeUp(); @@ -169,7 +178,8 @@ public final class WindowWakeUpPolicyTests { // Verify the policy wake up call succeeds because of the PowerManager wake up, since the // delegate would not handle the wake up request. - assertThat(mPolicy.wakeUpFromKey(300, KEYCODE_STEM_PRIMARY, false)).isTrue(); + assertThat(mPolicy.wakeUpFromKey( + DEFAULT_DISPLAY, 300, KEYCODE_STEM_PRIMARY, false)).isTrue(); verify(mInputWakeUpDelegate).wakeUpFromKey(300, KEYCODE_STEM_PRIMARY, false); verify(mPowerManager).wakeUp(300, WAKE_REASON_WAKE_KEY, "android.policy:KEY"); } @@ -186,7 +196,7 @@ public final class WindowWakeUpPolicyTests { .setInputWakeUpDelegate(mInputWakeUpDelegate); // Check that the wake up does not happen because the theater mode policy check fails. - assertThat(mPolicy.wakeUpFromKey(200, KEYCODE_POWER, true)).isFalse(); + assertThat(mPolicy.wakeUpFromKey(DEFAULT_DISPLAY, 200, KEYCODE_POWER, true)).isFalse(); verify(mInputWakeUpDelegate, never()).wakeUpFromKey(anyLong(), anyInt(), anyBoolean()); } @@ -201,11 +211,13 @@ public final class WindowWakeUpPolicyTests { .setInputWakeUpDelegate(mInputWakeUpDelegate); // Check that the wake up does not happen because the theater mode policy check fails. - assertThat(mPolicy.wakeUpFromMotion(200, SOURCE_TOUCHSCREEN, true)).isFalse(); + assertThat(mPolicy.wakeUpFromMotion( + mDefaultDisplay.getDisplayId(), 200, SOURCE_TOUCHSCREEN, true)).isFalse(); verify(mInputWakeUpDelegate, never()).wakeUpFromMotion(anyLong(), anyInt(), anyBoolean()); } @Test + @DisableFlags({FLAG_PER_DISPLAY_WAKE_BY_TOUCH}) public void testTheaterModeChecksNotAppliedWhenScreenIsOn() { mSetFlagsRule.enableFlags(FLAG_SUPPORT_INPUT_WAKEUP_DELEGATE); setDefaultDisplayState(Display.STATE_ON); @@ -213,30 +225,69 @@ public final class WindowWakeUpPolicyTests { setBooleanRes(config_allowTheaterModeWakeFromMotion, false); mPolicy = new WindowWakeUpPolicy(mContextSpy, mClock); - mPolicy.wakeUpFromMotion(200L, SOURCE_TOUCHSCREEN, true); + mPolicy.wakeUpFromMotion(mDefaultDisplay.getDisplayId(), 200L, SOURCE_TOUCHSCREEN, true); verify(mPowerManager).wakeUp(200L, WAKE_REASON_WAKE_MOTION, "android.policy:MOTION"); } @Test + @DisableFlags({FLAG_PER_DISPLAY_WAKE_BY_TOUCH}) public void testWakeUpFromMotion() { runPowerManagerUpChecks( - () -> mPolicy.wakeUpFromMotion(mClock.uptimeMillis(), SOURCE_TOUCHSCREEN, true), + () -> mPolicy.wakeUpFromMotion(mDefaultDisplay.getDisplayId(), + mClock.uptimeMillis(), SOURCE_TOUCHSCREEN, true), config_allowTheaterModeWakeFromMotion, WAKE_REASON_WAKE_MOTION, "android.policy:MOTION"); } @Test + @EnableFlags({FLAG_PER_DISPLAY_WAKE_BY_TOUCH}) + public void testWakeUpFromMotion_perDisplayWakeByTouchEnabled() { + setTheaterModeEnabled(false); + final int displayId = 555; + mPolicy = new WindowWakeUpPolicy(mContextSpy, mClock); + + boolean displayWokeUp = mPolicy.wakeUpFromMotion( + displayId, mClock.uptimeMillis(), SOURCE_TOUCHSCREEN, /* isDown= */ true); + + // Verify that display is woken up + assertThat(displayWokeUp).isTrue(); + verify(mPowerManager).wakeUp(anyLong(), eq(WAKE_REASON_WAKE_MOTION), + eq("android.policy:MOTION"), eq(displayId)); + } + + @Test + @DisableFlags({FLAG_PER_DISPLAY_WAKE_BY_TOUCH}) + public void testWakeUpFromMotion_perDisplayWakeByTouchDisabled() { + setTheaterModeEnabled(false); + final int displayId = 555; + mPolicy = new WindowWakeUpPolicy(mContextSpy, mClock); + + boolean displayWokeUp = mPolicy.wakeUpFromMotion( + displayId, mClock.uptimeMillis(), SOURCE_TOUCHSCREEN, /* isDown= */ true); + + // Verify that power is woken up and display isn't woken up individually + assertThat(displayWokeUp).isTrue(); + verify(mPowerManager).wakeUp( + anyLong(), eq(WAKE_REASON_WAKE_MOTION), eq("android.policy:MOTION")); + verify(mPowerManager, never()).wakeUp(anyLong(), eq(WAKE_REASON_WAKE_MOTION), + eq("android.policy:MOTION"), eq(displayId)); + } + + @Test + @DisableFlags({FLAG_PER_DISPLAY_WAKE_BY_TOUCH}) public void testWakeUpFromKey_nonPowerKey() { runPowerManagerUpChecks( - () -> mPolicy.wakeUpFromKey(mClock.uptimeMillis(), KEYCODE_HOME, true), + () -> mPolicy.wakeUpFromKey( + DEFAULT_DISPLAY, mClock.uptimeMillis(), KEYCODE_HOME, true), config_allowTheaterModeWakeFromKey, WAKE_REASON_WAKE_KEY, "android.policy:KEY"); } @Test + @DisableFlags({FLAG_PER_DISPLAY_WAKE_BY_TOUCH}) public void testWakeUpFromKey_powerKey() { // Disable the resource affecting all wake keys because it affects power key as well. // That way, power key wake during theater mode will solely be controlled by @@ -245,7 +296,8 @@ public final class WindowWakeUpPolicyTests { // Test with power key runPowerManagerUpChecks( - () -> mPolicy.wakeUpFromKey(mClock.uptimeMillis(), KEYCODE_POWER, true), + () -> mPolicy.wakeUpFromKey( + DEFAULT_DISPLAY, mClock.uptimeMillis(), KEYCODE_POWER, true), config_allowTheaterModeWakeFromPowerKey, WAKE_REASON_POWER_BUTTON, "android.policy:POWER"); @@ -254,13 +306,31 @@ public final class WindowWakeUpPolicyTests { // even if the power-key specific theater mode config is disabled. setBooleanRes(config_allowTheaterModeWakeFromPowerKey, false); runPowerManagerUpChecks( - () -> mPolicy.wakeUpFromKey(mClock.uptimeMillis(), KEYCODE_POWER, false), + () -> mPolicy.wakeUpFromKey( + DEFAULT_DISPLAY, mClock.uptimeMillis(), KEYCODE_POWER, false), config_allowTheaterModeWakeFromKey, WAKE_REASON_POWER_BUTTON, "android.policy:POWER"); } @Test + @EnableFlags({FLAG_PER_DISPLAY_WAKE_BY_TOUCH}) + public void testWakeUpFromKey_invalidDisplay_perDisplayWakeByTouchEnabled() { + setTheaterModeEnabled(false); + final int displayId = Display.INVALID_DISPLAY; + mPolicy = new WindowWakeUpPolicy(mContextSpy, mClock); + + boolean displayWokeUp = mPolicy.wakeUpFromKey( + displayId, mClock.uptimeMillis(), KEYCODE_POWER, /* isDown= */ false); + + // Verify that default display is woken up + assertThat(displayWokeUp).isTrue(); + verify(mPowerManager).wakeUp(anyLong(), eq(WAKE_REASON_POWER_BUTTON), + eq("android.policy:POWER"), eq(DEFAULT_DISPLAY)); + } + + @Test + @DisableFlags({FLAG_PER_DISPLAY_WAKE_BY_TOUCH}) public void testWakeUpFromLid() { runPowerManagerUpChecks( () -> mPolicy.wakeUpFromLid(), @@ -270,6 +340,7 @@ public final class WindowWakeUpPolicyTests { } @Test + @DisableFlags({FLAG_PER_DISPLAY_WAKE_BY_TOUCH}) public void testWakeUpFromWakeGesture() { runPowerManagerUpChecks( () -> mPolicy.wakeUpFromWakeGesture(), @@ -279,6 +350,7 @@ public final class WindowWakeUpPolicyTests { } @Test + @DisableFlags({FLAG_PER_DISPLAY_WAKE_BY_TOUCH}) public void testwakeUpFromCameraCover() { runPowerManagerUpChecks( () -> mPolicy.wakeUpFromCameraCover(mClock.uptimeMillis()), @@ -288,6 +360,7 @@ public final class WindowWakeUpPolicyTests { } @Test + @DisableFlags({FLAG_PER_DISPLAY_WAKE_BY_TOUCH}) public void testWakeUpFromPowerKeyCameraGesture() { // Disable the resource affecting all wake keys because it affects power key as well. // That way, power key wake during theater mode will solely be controlled by diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index c3466b9cee18..fee646d9cb9c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -3139,11 +3139,13 @@ public class ActivityRecordTests extends WindowTestsBase { @Test public void testOnStartingWindowDrawn() { + // Skip unnecessary resume top. + mSupervisor.beginDeferResume(); final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build(); // The task-has-been-visible should not affect the decision of making transition ready. activity.getTask().setHasBeenVisible(true); activity.detachFromProcess(); - activity.mStartingData = mock(StartingData.class); + activity.mStartingData = new SplashScreenStartingData(mWm, 0, 0); registerTestTransitionPlayer(); final Transition transition = activity.mTransitionController.requestTransitionIfNeeded( WindowManager.TRANSIT_OPEN, 0 /* flags */, null /* trigger */, mDisplayContent); @@ -3151,7 +3153,11 @@ public class ActivityRecordTests extends WindowTestsBase { assertTrue(activity.mStartingData.mIsDisplayed); // The transition can be ready by the starting window of a visible-requested activity // without a running process. - assertTrue(transition.allReady()); + if (!transition.allReady()) { + // Print unsatisfied conditions. + transition.onReadyTimeout(); + Assert.fail(transition + " must be ready by onStartingWindowDrawn"); + } // If other event makes the transition unready, the reentrant of onStartingWindowDrawn // should not replace the readiness again. diff --git a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerExemptionTests.java b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerExemptionTests.java index 965b65c8f1d1..ade591d006f5 100644 --- a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerExemptionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerExemptionTests.java @@ -23,11 +23,13 @@ import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_I import static com.android.server.wm.ActivityTaskManagerService.APP_SWITCH_ALLOW; import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_ALLOWLISTED_COMPONENT; import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_FOREGROUND; +import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_NON_APP_VISIBLE_WINDOW; import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_PERMISSION; import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_SAW_PERMISSION; import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_VISIBLE_WINDOW; import static com.android.server.wm.BackgroundActivityStartController.BAL_BLOCK; +import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; import static org.mockito.ArgumentMatchers.any; @@ -50,6 +52,7 @@ import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.provider.DeviceConfig; import android.util.Pair; +import android.view.WindowManager; import androidx.test.filters.SmallTest; @@ -126,6 +129,8 @@ public class BackgroundActivityStartControllerExemptionTests { AppOpsManager mAppOpsManager; MirrorActiveUids mActiveUids = new MirrorActiveUids(); WindowProcessControllerMap mProcessMap = new WindowProcessControllerMap(); + @Mock + VisibleActivityProcessTracker mVisibleActivityProcessTracker; @Mock ActivityTaskSupervisor mSupervisor; @@ -182,6 +187,8 @@ public class BackgroundActivityStartControllerExemptionTests { mService.mRootWindowContainer = mRootWindowContainer; when(mService.getAppOpsManager()).thenReturn(mAppOpsManager); setViaReflection(mService, "mProcessMap", mProcessMap); + setViaReflection(mService, "mVisibleActivityProcessTracker", + mVisibleActivityProcessTracker); setViaReflection(mSupervisor, "mRecentTasks", mRecentTasks); @@ -257,7 +264,7 @@ public class BackgroundActivityStartControllerExemptionTests { int realCallingPid = REGULAR_PID_2; // setup state - when(mService.hasActiveVisibleWindow(eq(callingUid))).thenReturn(true); + when(mVisibleActivityProcessTracker.hasVisibleActivity(eq(callingUid))).thenReturn(true); when(mService.getBalAppSwitchesState()).thenReturn(APP_SWITCH_ALLOW); // prepare call @@ -269,6 +276,8 @@ public class BackgroundActivityStartControllerExemptionTests { callingPid, callingPackage, realCallingUid, realCallingPid, mCallerApp, originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent, checkedOptions); + assertThat(balState.toString()).contains("callingUidHasVisibleActivity: true"); + assertThat(balState.toString()).contains("callingUidHasNonAppVisibleWindow: false"); // call BalVerdict callerVerdict = mController.checkBackgroundActivityStartAllowedByCaller( @@ -289,7 +298,8 @@ public class BackgroundActivityStartControllerExemptionTests { int realCallingPid = REGULAR_PID_2; // setup state - when(mService.hasActiveVisibleWindow(eq(realCallingUid))).thenReturn(true); + when(mVisibleActivityProcessTracker.hasVisibleActivity(eq(realCallingUid))).thenReturn( + true); when(mService.getBalAppSwitchesState()).thenReturn(APP_SWITCH_ALLOW); // prepare call @@ -301,6 +311,8 @@ public class BackgroundActivityStartControllerExemptionTests { callingPid, callingPackage, realCallingUid, realCallingPid, mCallerApp, originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent, checkedOptions); + assertThat(balState.toString()).contains("realCallingUidHasVisibleActivity: true"); + assertThat(balState.toString()).contains("realCallingUidHasNonAppVisibleWindow: false"); // call BalVerdict realCallerVerdict = mController.checkBackgroundActivityStartAllowedByRealCaller( @@ -313,6 +325,74 @@ public class BackgroundActivityStartControllerExemptionTests { } @Test + public void testCaller_appHasNonAppVisibleWindow() { + int callingUid = REGULAR_UID_1; + int callingPid = REGULAR_PID_1; + final String callingPackage = REGULAR_PACKAGE_1; + int realCallingUid = REGULAR_UID_2; + int realCallingPid = REGULAR_PID_2; + + // setup state + mActiveUids.onNonAppSurfaceVisibilityChanged(callingUid, true); + when(mService.getBalAppSwitchesState()).thenReturn(APP_SWITCH_ALLOW); + + // prepare call + PendingIntentRecord originatingPendingIntent = mPendingIntentRecord; + boolean allowBalExemptionForSystemProcess = false; + Intent intent = TEST_INTENT; + ActivityOptions checkedOptions = mCheckedOptions; + BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid, + callingPid, callingPackage, realCallingUid, realCallingPid, mCallerApp, + originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent, + checkedOptions); + assertThat(balState.toString()).contains("callingUidHasVisibleActivity: false"); + assertThat(balState.toString()).contains("callingUidHasNonAppVisibleWindow: true"); + + // call + BalVerdict callerVerdict = mController.checkBackgroundActivityStartAllowedByCaller( + balState); + balState.setResultForCaller(callerVerdict); + + // assertions + assertWithMessage(balState.toString()).that(callerVerdict.getCode()).isEqualTo( + BAL_ALLOW_NON_APP_VISIBLE_WINDOW); + } + + @Test + public void testRealCaller_appHasNonAppVisibleWindow() { + int callingUid = REGULAR_UID_1; + int callingPid = REGULAR_PID_1; + final String callingPackage = REGULAR_PACKAGE_1; + int realCallingUid = REGULAR_UID_2; + int realCallingPid = REGULAR_PID_2; + + // setup state + mActiveUids.onNonAppSurfaceVisibilityChanged(realCallingUid, true); + when(mService.getBalAppSwitchesState()).thenReturn(APP_SWITCH_ALLOW); + + // prepare call + PendingIntentRecord originatingPendingIntent = mPendingIntentRecord; + boolean allowBalExemptionForSystemProcess = false; + Intent intent = TEST_INTENT; + ActivityOptions checkedOptions = mCheckedOptions; + BackgroundActivityStartController.BalState balState = mController.new BalState(callingUid, + callingPid, callingPackage, realCallingUid, realCallingPid, mCallerApp, + originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent, + checkedOptions); + assertThat(balState.toString()).contains("realCallingUidHasVisibleActivity: false"); + assertThat(balState.toString()).contains("realCallingUidHasNonAppVisibleWindow: true"); + + // call + BalVerdict realCallerVerdict = mController.checkBackgroundActivityStartAllowedByRealCaller( + balState); + balState.setResultForRealCaller(realCallerVerdict); + + // assertions + assertWithMessage(balState.toString()).that(realCallerVerdict.getCode()).isEqualTo( + BAL_ALLOW_NON_APP_VISIBLE_WINDOW); + } + + @Test @RequiresFlagsEnabled(Flags.FLAG_BAL_ADDITIONAL_START_MODES) public void testCaller_appHasVisibleWindowWithIfVisibleOptIn() { int callingUid = REGULAR_UID_1; @@ -322,7 +402,7 @@ public class BackgroundActivityStartControllerExemptionTests { int realCallingPid = REGULAR_PID_2; // setup state - when(mService.hasActiveVisibleWindow(eq(callingUid))).thenReturn(true); + when(mVisibleActivityProcessTracker.hasVisibleActivity(eq(callingUid))).thenReturn(true); when(mService.getBalAppSwitchesState()).thenReturn(APP_SWITCH_ALLOW); // prepare call @@ -336,6 +416,8 @@ public class BackgroundActivityStartControllerExemptionTests { callingPid, callingPackage, realCallingUid, realCallingPid, mCallerApp, originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent, checkedOptions); + assertThat(balState.toString()).contains("callingUidHasVisibleActivity: true"); + assertThat(balState.toString()).contains("callingUidHasNonAppVisibleWindow: false"); // call BalVerdict callerVerdict = mController.checkBackgroundActivityStartAllowedByCaller( @@ -357,7 +439,8 @@ public class BackgroundActivityStartControllerExemptionTests { int realCallingPid = REGULAR_PID_2; // setup state - when(mService.hasActiveVisibleWindow(eq(realCallingUid))).thenReturn(true); + when(mVisibleActivityProcessTracker.hasVisibleActivity(eq(realCallingUid))).thenReturn( + true); when(mService.getBalAppSwitchesState()).thenReturn(APP_SWITCH_ALLOW); // prepare call @@ -371,6 +454,8 @@ public class BackgroundActivityStartControllerExemptionTests { callingPid, callingPackage, realCallingUid, realCallingPid, mCallerApp, originatingPendingIntent, allowBalExemptionForSystemProcess, mResultRecord, intent, checkedOptions); + assertThat(balState.toString()).contains("realCallingUidHasVisibleActivity: true"); + assertThat(balState.toString()).contains("realCallingUidHasNonAppVisibleWindow: false"); // call BalVerdict realCallerVerdict = mController.checkBackgroundActivityStartAllowedByRealCaller( diff --git a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerLogTests.java b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerLogTests.java index 7f7462debe8f..99e730ae76cf 100644 --- a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerLogTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerLogTests.java @@ -67,12 +67,16 @@ public class BackgroundActivityStartControllerLogTests { @Mock PendingIntentRecord mPendingIntentRecord; MirrorActiveUids mActiveUids = new MirrorActiveUids(); + @Mock + VisibleActivityProcessTracker mVisibleActivityProcessTracker; BackgroundActivityStartController mController; BackgroundActivityStartController.BalState mState; @Before public void setup() { setViaReflection(mService, "mActiveUids", mActiveUids); + setViaReflection(mService, "mVisibleActivityProcessTracker", + mVisibleActivityProcessTracker); mController = new BackgroundActivityStartController(mService, mSupervisor); } diff --git a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java index db3ce0bc98eb..854bda03f18d 100644 --- a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java @@ -111,6 +111,9 @@ public class BackgroundActivityStartControllerTests { @Mock AppOpsManager mAppOpsManager; MirrorActiveUids mActiveUids = new MirrorActiveUids(); + @Mock + VisibleActivityProcessTracker mVisibleActivityProcessTracker; + WindowProcessControllerMap mProcessMap = new WindowProcessControllerMap(); @Mock @@ -200,6 +203,8 @@ public class BackgroundActivityStartControllerTests { mService.mRootWindowContainer = mRootWindowContainer; Mockito.when(mService.getAppOpsManager()).thenReturn(mAppOpsManager); setViaReflection(mService, "mProcessMap", mProcessMap); + setViaReflection(mService, "mVisibleActivityProcessTracker", + mVisibleActivityProcessTracker); //Mockito.when(mSupervisor.getBackgroundActivityLaunchController()).thenReturn(mController); setViaReflection(mSupervisor, "mRecentTasks", mRecentTasks); @@ -551,13 +556,14 @@ public class BackgroundActivityStartControllerTests { assertThat(balState.callerExplicitOptInOrOut()).isFalse(); assertThat(balState.realCallerExplicitOptInOrAutoOptIn()).isTrue(); assertThat(balState.realCallerExplicitOptInOrOut()).isFalse(); - assertThat(balState.toString()).startsWith( + assertThat(balState.toString()).isEqualTo( "[callingPackage: package.app1; " + "callingPackageTargetSdk: -1; " + "callingUid: 10001; " + "callingPid: 11001; " + "appSwitchState: 0; " - + "callingUidHasAnyVisibleWindow: false; " + + "callingUidHasVisibleActivity: false; " + + "callingUidHasNonAppVisibleWindow: false; " + "callingUidProcState: NONEXISTENT; " + "isCallingUidPersistentSystemProcess: false; " + "allowBalExemptionForSystemProcess: false; " @@ -576,13 +582,17 @@ public class BackgroundActivityStartControllerTests { + "realCallingPackageTargetSdk: -1; " + "realCallingUid: 1; " + "realCallingPid: 1; " - + "realCallingUidHasAnyVisibleWindow: false; " + + "realCallingUidHasVisibleActivity: false; " + + "realCallingUidHasNonAppVisibleWindow: false; " + "realCallingUidProcState: NONEXISTENT; " + "isRealCallingUidPersistentSystemProcess: false; " + "originatingPendingIntent: null; " + "realCallerApp: null; " + "balAllowedByPiSender: BSP.ALLOW_BAL; " - + "resultIfPiSenderAllowsBal: null"); + + "resultIfPiSenderAllowsBal: null; " + + "realCallerStartMode: MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED; " + + "balRequireOptInByPendingIntentCreator: true; " + + "balDontBringExistingBackgroundTaskStackToFg: true]"); } @Test @@ -651,13 +661,14 @@ public class BackgroundActivityStartControllerTests { assertThat(balState.callerExplicitOptInOrOut()).isFalse(); assertThat(balState.realCallerExplicitOptInOrAutoOptIn()).isFalse(); assertThat(balState.realCallerExplicitOptInOrOut()).isFalse(); - assertThat(balState.toString()).startsWith( + assertThat(balState.toString()).isEqualTo( "[callingPackage: package.app1; " + "callingPackageTargetSdk: -1; " + "callingUid: 10001; " + "callingPid: 11001; " + "appSwitchState: 0; " - + "callingUidHasAnyVisibleWindow: false; " + + "callingUidHasVisibleActivity: false; " + + "callingUidHasNonAppVisibleWindow: false; " + "callingUidProcState: NONEXISTENT; " + "isCallingUidPersistentSystemProcess: false; " + "allowBalExemptionForSystemProcess: false; " @@ -676,12 +687,16 @@ public class BackgroundActivityStartControllerTests { + "realCallingPackageTargetSdk: -1; " + "realCallingUid: 1; " + "realCallingPid: 1; " - + "realCallingUidHasAnyVisibleWindow: false; " + + "realCallingUidHasVisibleActivity: false; " + + "realCallingUidHasNonAppVisibleWindow: false; " + "realCallingUidProcState: NONEXISTENT; " + "isRealCallingUidPersistentSystemProcess: false; " + "originatingPendingIntent: PendingIntentRecord; " + "realCallerApp: null; " + "balAllowedByPiSender: BSP.ALLOW_FGS; " - + "resultIfPiSenderAllowsBal: null"); + + "resultIfPiSenderAllowsBal: null; " + + "realCallerStartMode: MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED; " + + "balRequireOptInByPendingIntentCreator: true; " + + "balDontBringExistingBackgroundTaskStackToFg: true]"); } } 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 62a471166c5b..41f1e2359c88 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java @@ -87,6 +87,7 @@ import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doCallRealMethod; import static org.mockito.Mockito.times; +import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityManagerInternal; @@ -4782,6 +4783,114 @@ public class SizeCompatTests extends WindowTestsBase { } @Test + @EnableFlags(Flags.FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING) + public void testCameraCompatAspectRatioAppliedForFixedOrientationCameraActivities() { + // Needed to create camera compat policy in DisplayContent. + allowDesktopMode(); + // Create display that has all stable insets and does not rotate. + final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1080, 600) + .setSystemDecorations(true).setCanRotate(false).build(); + + final float cameraCompatAspectRatio = 4.0f; + setupCameraCompatAspectRatio(cameraCompatAspectRatio, display); + + // Create task on test display. + final Task task = new TaskBuilder(mSupervisor).setDisplay(display).build(); + + // Create fixed portrait activity. + final ActivityRecord fixedOrientationActivity = new ActivityBuilder(mAtm) + .setTask(task).setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT).build(); + final Rect fixedOrientationAppBounds = new Rect(fixedOrientationActivity.getConfiguration() + .windowConfiguration.getAppBounds()); + + assertEquals(cameraCompatAspectRatio, computeAspectRatio(fixedOrientationAppBounds), + DELTA_ASPECT_RATIO_TOLERANCE); + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING) + public void testCameraCompatAspectRatioForFixedOrientationCameraActivitiesPortraitWindow() { + // Needed to create camera compat policy in DisplayContent. + allowDesktopMode(); + // Create portrait display that has all stable insets and does not rotate. + final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1080, 1600) + .setSystemDecorations(true).setCanRotate(false).build(); + + final float cameraCompatAspectRatio = 4.0f; + setupCameraCompatAspectRatio(cameraCompatAspectRatio, display); + + // Create task on test display. + final Task task = new TaskBuilder(mSupervisor).setDisplay(display).build(); + + // Create fixed portrait activity. + final ActivityRecord fixedOrientationActivity = new ActivityBuilder(mAtm) + .setTask(task).setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT).build(); + final Rect fixedOrientationAppBounds = new Rect(fixedOrientationActivity.getConfiguration() + .windowConfiguration.getAppBounds()); + + assertEquals(cameraCompatAspectRatio, computeAspectRatio(fixedOrientationAppBounds), + DELTA_ASPECT_RATIO_TOLERANCE); + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING) + public void testCameraCompatAspectRatioAppliedInsteadOfDefaultAspectRatio() { + // Needed to create camera compat policy in DisplayContent. + allowDesktopMode(); + // Create display that has all stable insets and does not rotate. + final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1080, 600) + .setSystemDecorations(true).setCanRotate(false).build(); + + final float cameraCompatAspectRatio = 5.0f; + setupCameraCompatAspectRatio(cameraCompatAspectRatio, display); + + // Create task on test display. + final Task task = new TaskBuilder(mSupervisor).setDisplay(display).build(); + + // App's target min aspect ratio - this should not be used, as camera controls aspect ratio. + final float targetMinAspectRatio = 4.0f; + + // Create fixed portrait activity with min aspect ratio greater than parent aspect ratio. + final ActivityRecord minAspectRatioActivity = new ActivityBuilder(mAtm) + .setTask(task).setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT) + .setMinAspectRatio(targetMinAspectRatio).build(); + final Rect minAspectRatioAppBounds = new Rect(minAspectRatioActivity.getConfiguration() + .windowConfiguration.getAppBounds()); + + assertEquals(cameraCompatAspectRatio, computeAspectRatio(minAspectRatioAppBounds), + DELTA_ASPECT_RATIO_TOLERANCE); + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING) + public void testCameraCompatAspectRatio_defualtAspectRatioAppliedWhenGreater() { + // Needed to create camera compat policy in DisplayContent. + allowDesktopMode(); + // Create display that has all stable insets and does not rotate. + final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1080, 600) + .setSystemDecorations(true).setCanRotate(false).build(); + + final float cameraCompatAspectRatio = 5.0f; + setupCameraCompatAspectRatio(cameraCompatAspectRatio, display); + + // Create task on test display. + final Task task = new TaskBuilder(mSupervisor).setDisplay(display).build(); + + // App's target min aspect ratio bigger than camera compat aspect ratio - use that instead. + final float targetMinAspectRatio = 6.0f; + + // Create fixed portrait activity with min aspect ratio greater than parent aspect ratio. + final ActivityRecord minAspectRatioActivity = new ActivityBuilder(mAtm) + .setTask(task).setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT) + .setMinAspectRatio(targetMinAspectRatio).build(); + final Rect minAspectRatioAppBounds = new Rect(minAspectRatioActivity.getConfiguration() + .windowConfiguration.getAppBounds()); + + assertEquals(targetMinAspectRatio, computeAspectRatio(minAspectRatioAppBounds), + DELTA_ASPECT_RATIO_TOLERANCE); + } + + @Test public void testUniversalResizeable() { mWm.mConstants.mIgnoreActivityOrientationRequest = true; setUpApp(mDisplayContent); @@ -4868,6 +4977,25 @@ public class SizeCompatTests extends WindowTestsBase { assertEquals(newDensity, mActivity.getConfiguration().densityDpi); } + /** + * {@code canEnterDesktopMode} is called when {@link CameraCompatFreeformPolicy} is created in + * {@link AppCompatCameraPolicy}. + * + * <p>{@link #allowDesktopMode()} needs to be called before {@link DisplayContent} is created. + */ + private void allowDesktopMode() { + doReturn(true).when(() -> DesktopModeHelper.canEnterDesktopMode(any())); + } + + private void setupCameraCompatAspectRatio(float cameraCompatAspectRatio, + @NonNull DisplayContent display) { + CameraCompatFreeformPolicy cameraPolicy = display.mAppCompatCameraPolicy + .mCameraCompatFreeformPolicy; + spyOn(cameraPolicy); + doReturn(true).when(cameraPolicy).shouldCameraCompatControlAspectRatio(any()); + doReturn(cameraCompatAspectRatio).when(cameraPolicy).getCameraCompatAspectRatio(any()); + } + private void setUpAllowThinLetterboxed(boolean thinLetterboxAllowed) { final AppCompatReachabilityOverrides reachabilityOverrides = mActivity.mAppCompatController.getAppCompatReachabilityOverrides(); diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java index 9967ccebeb1f..7dba1422d61d 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java @@ -21,7 +21,6 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyZeroInteractions; import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION; import static org.junit.Assert.assertEquals; @@ -165,31 +164,6 @@ public class SurfaceAnimatorTest extends WindowTestsBase { } @Test - public void testDelayingAnimationStart() { - mAnimatable.mSurfaceAnimator.startDelayingAnimationStart(); - mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */, - ANIMATION_TYPE_APP_TRANSITION); - verifyZeroInteractions(mSpec); - assertAnimating(mAnimatable); - assertTrue(mAnimatable.mSurfaceAnimator.isAnimationStartDelayed()); - mAnimatable.mSurfaceAnimator.endDelayingAnimationStart(); - verify(mSpec).startAnimation(any(), any(), eq(ANIMATION_TYPE_APP_TRANSITION), any()); - } - - @Test - public void testDelayingAnimationStartAndCancelled() { - mAnimatable.mSurfaceAnimator.startDelayingAnimationStart(); - mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */, - ANIMATION_TYPE_APP_TRANSITION); - mAnimatable.mSurfaceAnimator.cancelAnimation(); - verifyZeroInteractions(mSpec); - assertNotAnimating(mAnimatable); - assertTrue(mAnimatable.mFinishedCallbackCalled); - assertEquals(ANIMATION_TYPE_APP_TRANSITION, mAnimatable.mFinishedAnimationType); - verify(mTransaction).remove(eq(mAnimatable.mLeash)); - } - - @Test public void testTransferAnimation() { mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */, ANIMATION_TYPE_APP_TRANSITION); diff --git a/telecomm/java/android/telecom/Log.java b/telecomm/java/android/telecom/Log.java index a34094ce6452..98949d0c45cf 100644 --- a/telecomm/java/android/telecom/Log.java +++ b/telecomm/java/android/telecom/Log.java @@ -68,7 +68,7 @@ public class Log { // Used to synchronize singleton logging lazy initialization private static final Object sSingletonSync = new Object(); private static EventManager sEventManager; - private static SessionManager sSessionManager; + private static volatile SessionManager sSessionManager; private static Object sLock = null; /** @@ -379,6 +379,23 @@ public class Log { return sSessionManager; } + @VisibleForTesting + public static SessionManager setSessionManager(Context context, + java.lang.Runnable cleanSessionRunnable) { + // Checking for null again outside of synchronization because we only need to synchronize + // during the lazy loading of the session logger. We don't need to synchronize elsewhere. + if (sSessionManager == null) { + synchronized (sSingletonSync) { + if (sSessionManager == null) { + sSessionManager = new SessionManager(cleanSessionRunnable); + sSessionManager.setContext(context); + return sSessionManager; + } + } + } + return sSessionManager; + } + public static void setTag(String tag) { TAG = tag; DEBUG = isLoggable(android.util.Log.DEBUG); diff --git a/telecomm/java/android/telecom/Logging/SessionManager.java b/telecomm/java/android/telecom/Logging/SessionManager.java index 00e344c67cc5..ac1e69e92ec0 100644 --- a/telecomm/java/android/telecom/Logging/SessionManager.java +++ b/telecomm/java/android/telecom/Logging/SessionManager.java @@ -62,9 +62,7 @@ public class SessionManager { @VisibleForTesting public final ConcurrentHashMap<Integer, Session> mSessionMapper = new ConcurrentHashMap<>(64); - @VisibleForTesting - public java.lang.Runnable mCleanStaleSessions = () -> - cleanupStaleSessions(getSessionCleanupTimeoutMs()); + private final java.lang.Runnable mCleanStaleSessions; private final Handler mSessionCleanupHandler = new Handler(Looper.getMainLooper()); // Overridden in LogTest to skip query to ContentProvider @@ -110,29 +108,39 @@ public class SessionManager { } public SessionManager() { + mCleanStaleSessions = () -> cleanupStaleSessions(getSessionCleanupTimeoutMs()); + } + + @VisibleForTesting + public SessionManager(java.lang.Runnable cleanStaleSessionsRunnable) { + mCleanStaleSessions = cleanStaleSessionsRunnable; } private long getSessionCleanupTimeoutMs() { return mSessionCleanupTimeoutMs.get(); } - private synchronized void resetStaleSessionTimer() { + private void resetStaleSessionTimer() { if (!Flags.endSessionImprovements()) { - mSessionCleanupHandler.removeCallbacksAndMessages(null); - // Will be null in Log Testing - if (mCleanStaleSessions != null) { - mSessionCleanupHandler.postDelayed(mCleanStaleSessions, - getSessionCleanupTimeoutMs()); - } - } else { - if (mCleanStaleSessions != null - && !mSessionCleanupHandler.hasCallbacks(mCleanStaleSessions)) { + resetStaleSessionTimerOld(); + return; + } + // Will be null in Log Testing + if (mCleanStaleSessions == null) return; + synchronized (mSessionCleanupHandler) { + if (!mSessionCleanupHandler.hasCallbacks(mCleanStaleSessions)) { mSessionCleanupHandler.postDelayed(mCleanStaleSessions, getSessionCleanupTimeoutMs()); } } } + private synchronized void resetStaleSessionTimerOld() { + if (mCleanStaleSessions == null) return; + mSessionCleanupHandler.removeCallbacksAndMessages(null); + mSessionCleanupHandler.postDelayed(mCleanStaleSessions, getSessionCleanupTimeoutMs()); + } + /** * Determines whether or not to start a new session or continue an existing session based on * the {@link Session.Info} info passed into startSession. If info is null, a new Session is diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 2a06c3da0195..6490cbe3e31a 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -2183,8 +2183,8 @@ public class CarrierConfigManager { * Maximum size in bytes of the PDU to send or download when connected to a non-terrestrial * network. MmsService will return a result code of MMS_ERROR_TOO_LARGE_FOR_TRANSPORT if * the PDU exceeds this limit when connected to a non-terrestrial network. - * @hide */ + @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS) public static final String KEY_MMS_MAX_NTN_PAYLOAD_SIZE_BYTES_INT = "mms_max_ntn_payload_size_bytes_int"; @@ -9850,9 +9850,8 @@ public class CarrierConfigManager { * manually scanning available cellular network. * If key is {@code true}, satellite plmn should not be exposed to user and should be * automatically set, {@code false} otherwise. Default value is {@code true}. - * - * @hide */ + @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS) public static final String KEY_REMOVE_SATELLITE_PLMN_IN_MANUAL_NETWORK_SCAN_BOOL = "remove_satellite_plmn_in_manual_network_scan_bool"; @@ -9877,18 +9876,18 @@ public class CarrierConfigManager { /** * Doesn't support unrestricted traffic on satellite network. - * @hide */ + @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS) public static final int SATELLITE_DATA_SUPPORT_ONLY_RESTRICTED = 0; /** * Support unrestricted but bandwidth_constrained traffic on satellite network. - * @hide */ + @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS) public static final int SATELLITE_DATA_SUPPORT_BANDWIDTH_CONSTRAINED = 1; /** * Support unrestricted satellite network that serves all traffic. - * @hide */ + @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS) public static final int SATELLITE_DATA_SUPPORT_ALL = 2; /** * Indicates what kind of traffic an {@link NetworkCapabilities#NET_CAPABILITY_NOT_RESTRICTED} @@ -9898,8 +9897,8 @@ public class CarrierConfigManager { * {@link ApnSetting#INFRASTRUCTURE_SATELLITE} from APN infrastructure_bitmask, and this * configuration is ignored. * By default it only supports restricted data. - * @hide */ + @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS) public static final String KEY_SATELLITE_DATA_SUPPORT_MODE_INT = "satellite_data_support_mode_int"; @@ -9911,8 +9910,8 @@ public class CarrierConfigManager { * {@link com.android.ims.ImsConfig.WfcModeFeatureValueConstants#WIFI_PREFERRED} * {@code false} - roaming preference can be changed by user independently and is not * overridden when device is connected to non-terrestrial network. - * @hide */ + @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS) public static final String KEY_OVERRIDE_WFC_ROAMING_MODE_WHILE_USING_NTN_BOOL = "override_wfc_roaming_mode_while_using_ntn_bool"; @@ -9945,8 +9944,8 @@ public class CarrierConfigManager { * Reference: GSMA TS.43-v11, 2.8.5 Fast Authentication and Token Management. * `app_name` is an optional attribute in the request and may vary depending on the carrier * requirement. - * @hide */ + @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS) public static final String KEY_SATELLITE_ENTITLEMENT_APP_NAME_STRING = "satellite_entitlement_app_name_string"; @@ -9954,9 +9953,8 @@ public class CarrierConfigManager { * URL to redirect user to get more information about the carrier support for satellite. * * The default value is empty string. - * - * @hide */ + @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS) public static final String KEY_SATELLITE_INFORMATION_REDIRECT_URL_STRING = "satellite_information_redirect_url_string"; /** @@ -9966,9 +9964,8 @@ public class CarrierConfigManager { * This will need agreement with carriers before enabling this flag. * * The default value is false. - * - * @hide */ + @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS) public static final String KEY_EMERGENCY_MESSAGING_SUPPORTED_BOOL = "emergency_messaging_supported_bool"; @@ -9983,9 +9980,8 @@ public class CarrierConfigManager { * prompt user to switch to using satellite emergency messaging. * * The default value is 30 seconds. - * - * @hide */ + @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS) public static final String KEY_EMERGENCY_CALL_TO_SATELLITE_T911_HANDOVER_TIMEOUT_MILLIS_INT = "emergency_call_to_satellite_t911_handover_timeout_millis_int"; @@ -9998,9 +9994,8 @@ public class CarrierConfigManager { * The default capabilities are * {@link NetworkRegistrationInfo#SERVICE_TYPE_SMS}, and * {@link NetworkRegistrationInfo#SERVICE_TYPE_MMS} - * - * @hide */ + @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS) public static final String KEY_CARRIER_ROAMING_SATELLITE_DEFAULT_SERVICES_INT_ARRAY = "carrier_roaming_satellite_default_services_int_array"; @@ -10031,9 +10026,8 @@ public class CarrierConfigManager { * Defines the NIDD (Non-IP Data Delivery) APN to be used for carrier roaming to satellite * attachment. For more on NIDD, see 3GPP TS 29.542. * Note this config is the only source of truth regarding the definition of the APN. - * - * @hide */ + @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS) public static final String KEY_SATELLITE_NIDD_APN_NAME_STRING = "satellite_nidd_apn_name_string"; @@ -10044,9 +10038,8 @@ public class CarrierConfigManager { * * If {@code false}, the emergency call is always blocked if device is in emergency satellite * mode. Note if device is NOT in emergency satellite mode, emergency call is always allowed. - * - * @hide */ + @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS) public static final String KEY_SATELLITE_ROAMING_TURN_OFF_SESSION_FOR_EMERGENCY_CALL_BOOL = "satellite_roaming_turn_off_session_for_emergency_call_bool"; @@ -10059,14 +10052,14 @@ public class CarrierConfigManager { /** * Device can connect to carrier roaming non-terrestrial network automatically. - * @hide */ + @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS) public static final int CARRIER_ROAMING_NTN_CONNECT_AUTOMATIC = 0; /** * Device can connect to carrier roaming non-terrestrial network only if user manually triggers * satellite connection. - * @hide */ + @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS) public static final int CARRIER_ROAMING_NTN_CONNECT_MANUAL = 1; /** * Indicates carrier roaming non-terrestrial network connect type that the device can use to @@ -10074,8 +10067,8 @@ public class CarrierConfigManager { * If this key is set to CARRIER_ROAMING_NTN_CONNECT_MANUAL then connect button will be * displayed to user when the device is eligible to use carrier roaming * non-terrestrial network. - * @hide */ + @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS) public static final String KEY_CARRIER_ROAMING_NTN_CONNECT_TYPE_INT = "carrier_roaming_ntn_connect_type_int"; @@ -10088,7 +10081,6 @@ public class CarrierConfigManager { * will be made to T911. * * The default value is {@link SatelliteManager#EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_T911}. - * */ @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN) public static final String @@ -10105,9 +10097,8 @@ public class CarrierConfigManager { * After the timer is expired, device is marked as eligible for satellite communication. * * The default value is 180 seconds. - * - * @hide */ + @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS) public static final String KEY_CARRIER_SUPPORTED_SATELLITE_NOTIFICATION_HYSTERESIS_SEC_INT = "carrier_supported_satellite_notification_hysteresis_sec_int"; @@ -10153,7 +10144,9 @@ public class CarrierConfigManager { "satellite_roaming_esos_inactivity_timeout_sec_int"; /** - * A string array containing the list of messaging package names that support satellite. + * A string array containing the list of messaging apps that support satellite. + * + * The default value contains only "com.google.android.apps.messaging" * * @hide */ @@ -10166,9 +10159,8 @@ public class CarrierConfigManager { * the default APN (i.e. internet) will be used for tethering. * * This config is only available when using Preset APN(not user edited) as Preferred APN. - * - * @hide */ + @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS) public static final String KEY_DISABLE_DUN_APN_WHILE_ROAMING_WITH_PRESET_APN_BOOL = "disable_dun_apn_while_roaming_with_preset_apn_bool"; @@ -11313,6 +11305,8 @@ public class CarrierConfigManager { NetworkRegistrationInfo.SERVICE_TYPE_SMS, NetworkRegistrationInfo.SERVICE_TYPE_MMS }); + sDefaults.putStringArray(KEY_SATELLITE_SUPPORTED_MSG_APPS_STRING_ARRAY, new String[]{ + "com.google.android.apps.messaging"}); sDefaults.putBoolean(KEY_DISABLE_DUN_APN_WHILE_ROAMING_WITH_PRESET_APN_BOOL, false); sDefaults.putBoolean(KEY_EMERGENCY_MESSAGING_SUPPORTED_BOOL, false); sDefaults.putInt(KEY_EMERGENCY_CALL_TO_SATELLITE_T911_HANDOVER_TIMEOUT_MILLIS_INT, diff --git a/telephony/java/android/telephony/satellite/EarfcnRange.aidl b/telephony/java/android/telephony/satellite/EarfcnRange.aidl new file mode 100644 index 000000000000..0b224d0b09bd --- /dev/null +++ b/telephony/java/android/telephony/satellite/EarfcnRange.aidl @@ -0,0 +1,19 @@ +/* + * Copyright 2024, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.satellite; + +parcelable EarfcnRange; diff --git a/telephony/java/android/telephony/satellite/EarfcnRange.java b/telephony/java/android/telephony/satellite/EarfcnRange.java new file mode 100644 index 000000000000..38043b570c2f --- /dev/null +++ b/telephony/java/android/telephony/satellite/EarfcnRange.java @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.telephony.satellite; + +import android.annotation.FlaggedApi; +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +import com.android.internal.telephony.flags.Flags; + +/** + * EARFCN (E-UTRA Absolute Radio Frequency Channel Number): A number that identifies a + * specific frequency channel in LTE/5G NR, used to define the carrier frequency. + * The range can be [0 ~ 65535] according to the 3GPP TS 36.101 + * + * In satellite communication: + * - Efficient frequency allocation across a wide coverage area. + * - Handles Doppler shift due to satellite movement. + * - Manages interference with terrestrial networks. + * + * See 3GPP TS 36.101 and 38.101-1 for details. + * + * @hide + */ +@FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN) +public final class EarfcnRange implements Parcelable { + + /** + * The start frequency of the earfcn range and is inclusive in the range + */ + private int mStartEarfcn; + + /** + * The end frequency of the earfcn range and is inclusive in the range. + */ + private int mEndEarfcn; + + private EarfcnRange(@NonNull Parcel in) { + readFromParcel(in); + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mStartEarfcn); + dest.writeInt(mEndEarfcn); + } + + private void readFromParcel(Parcel in) { + mStartEarfcn = in.readInt(); + mEndEarfcn = in.readInt(); + } + + /** + * Constructor for the EarfcnRange class. + * The range can be [0 ~ 65535] according to the 3GPP TS 36.101 + * + * @param startEarfcn The starting earfcn value. + * @param endEarfcn The ending earfcn value. + */ + public EarfcnRange(@IntRange(from = 0, to = 65535) int endEarfcn, + @IntRange(from = 0, to = 65535) int startEarfcn) { + mEndEarfcn = endEarfcn; + mStartEarfcn = startEarfcn; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public String toString() { + return "startEarfcn: " + mStartEarfcn + ", " + "endEarfcn: " + mEndEarfcn; + } + + @NonNull + public static final Creator<EarfcnRange> CREATOR = new Creator<EarfcnRange>() { + @Override + public EarfcnRange createFromParcel(Parcel in) { + return new EarfcnRange(in); + } + + @Override + public EarfcnRange[] newArray(int size) { + return new EarfcnRange[size]; + } + }; + + /** + * Returns the starting earfcn value for this range. + * It can be [0 ~ 65535] according to the 3GPP TS 36.101 + * + * @return The starting earfcn. + */ + public @IntRange(from = 0, to = 65535) int getStartEarfcn() { + return mStartEarfcn; + } + + /** + * Returns the ending earfcn value for this range. + * It can be [0 ~ 65535] according to the 3GPP TS 36.101 + * + * @return The ending earfcn. + */ + public @IntRange(from = 0, to = 65535) int getEndEarfcn() { + return mEndEarfcn; + } +} diff --git a/telephony/java/android/telephony/satellite/ISatelliteCommunicationAllowedStateCallback.aidl b/telephony/java/android/telephony/satellite/ISatelliteCommunicationAllowedStateCallback.aidl index a7eda482cb76..2730f90c4e5e 100644 --- a/telephony/java/android/telephony/satellite/ISatelliteCommunicationAllowedStateCallback.aidl +++ b/telephony/java/android/telephony/satellite/ISatelliteCommunicationAllowedStateCallback.aidl @@ -16,6 +16,8 @@ package android.telephony.satellite; +import android.telephony.satellite.SatelliteAccessConfiguration; + /** * Interface for satellite communication allowed state callback. * @hide @@ -29,4 +31,14 @@ oneway interface ISatelliteCommunicationAllowedStateCallback { * @param allowed whether satellite communication state or not */ void onSatelliteCommunicationAllowedStateChanged(in boolean isAllowed); + + /** + * Callback method invoked when the satellite access configuration changes + * + * @param The satellite access configuration associated with the current location. + * When satellite is not allowed at the current location, + * {@code satelliteRegionalConfiguration} will be null. + */ + void onSatelliteAccessConfigurationChanged(in SatelliteAccessConfiguration + satelliteAccessConfiguration); } diff --git a/telephony/java/android/telephony/satellite/SatelliteAccessConfiguration.aidl b/telephony/java/android/telephony/satellite/SatelliteAccessConfiguration.aidl new file mode 100644 index 000000000000..0214193a654f --- /dev/null +++ b/telephony/java/android/telephony/satellite/SatelliteAccessConfiguration.aidl @@ -0,0 +1,19 @@ +/* + * Copyright 2024, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + package android.telephony.satellite; + + parcelable SatelliteAccessConfiguration;
\ No newline at end of file diff --git a/telephony/java/android/telephony/satellite/SatelliteAccessConfiguration.java b/telephony/java/android/telephony/satellite/SatelliteAccessConfiguration.java new file mode 100644 index 000000000000..c3ae70b48854 --- /dev/null +++ b/telephony/java/android/telephony/satellite/SatelliteAccessConfiguration.java @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.satellite; + +import android.annotation.FlaggedApi; +import android.os.Parcel; +import android.os.Parcelable; + +import androidx.annotation.NonNull; + +import com.android.internal.telephony.flags.Flags; + +import java.util.List; + +/** + * SatelliteAccessConfiguration is used to store satellite access configuration + * that will be applied to the satellite communication at the corresponding region. + * + * @hide + */ +@FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN) +public final class SatelliteAccessConfiguration implements Parcelable { + /** + * The list of satellites available at the current location. + */ + @NonNull + private List<SatelliteInfo> mSatelliteInfoList; + + /** + * The list of tag IDs associated with the current location + */ + @NonNull + private int[] mTagIds; + + /** + * Constructor for {@link SatelliteAccessConfiguration}. + * + * @param satelliteInfos The list of {@link SatelliteInfo} objects representing the satellites + * accessible with this configuration. + * @param tagIds The list of tag IDs associated with this configuration. + */ + public SatelliteAccessConfiguration(@NonNull List<SatelliteInfo> satelliteInfos, + @NonNull int[] tagIds) { + mSatelliteInfoList = satelliteInfos; + mTagIds = tagIds; + } + + public SatelliteAccessConfiguration(Parcel in) { + mSatelliteInfoList = in.createTypedArrayList(SatelliteInfo.CREATOR); + mTagIds = new int[in.readInt()]; + in.readIntArray(mTagIds); + } + + public static final Creator<SatelliteAccessConfiguration> CREATOR = + new Creator<SatelliteAccessConfiguration>() { + @Override + public SatelliteAccessConfiguration createFromParcel(Parcel in) { + return new SatelliteAccessConfiguration(in); + } + + @Override + public SatelliteAccessConfiguration[] newArray(int size) { + return new SatelliteAccessConfiguration[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + /** + * @param dest The Parcel in which the object should be written. + * @param flags Additional flags about how the object should be written. + * May be 0 or {@link #PARCELABLE_WRITE_RETURN_VALUE}. + */ + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeTypedList(mSatelliteInfoList); + if (mTagIds != null && mTagIds.length > 0) { + dest.writeInt(mTagIds.length); + dest.writeIntArray(mTagIds); + } else { + dest.writeInt(0); + } + } + + /** + * Returns a list of {@link SatelliteInfo} objects representing the satellites + * associated with this object. + * + * @return The list of {@link SatelliteInfo} objects. + */ + @NonNull + public List<SatelliteInfo> getSatelliteInfos() { + return mSatelliteInfoList; + } + + /** + * Returns a list of tag IDs associated with this object. + * + * @return The list of tag IDs. + */ + @NonNull + public int[] getTagIds() { + return mTagIds; + } +} diff --git a/telephony/java/android/telephony/satellite/SatelliteCommunicationAllowedStateCallback.java b/telephony/java/android/telephony/satellite/SatelliteCommunicationAllowedStateCallback.java index 1a870202d096..bffb11f23d56 100644 --- a/telephony/java/android/telephony/satellite/SatelliteCommunicationAllowedStateCallback.java +++ b/telephony/java/android/telephony/satellite/SatelliteCommunicationAllowedStateCallback.java @@ -17,6 +17,7 @@ package android.telephony.satellite; import android.annotation.FlaggedApi; +import android.annotation.Nullable; import com.android.internal.telephony.flags.Flags; @@ -40,4 +41,17 @@ public interface SatelliteCommunicationAllowedStateCallback { */ @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) void onSatelliteCommunicationAllowedStateChanged(boolean isAllowed); + + /** + * Callback method invoked when the satellite access configuration changes + * + * @param satelliteAccessConfiguration The satellite access configuration associated with + * the current location. When satellite is not allowed at + * the current location, + * {@code satelliteRegionalConfiguration} will be null. + * @hide + */ + @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN) + default void onSatelliteAccessConfigurationChanged( + @Nullable SatelliteAccessConfiguration satelliteAccessConfiguration) {}; } diff --git a/telephony/java/android/telephony/satellite/SatelliteInfo.aidl b/telephony/java/android/telephony/satellite/SatelliteInfo.aidl new file mode 100644 index 000000000000..fc2303b080a5 --- /dev/null +++ b/telephony/java/android/telephony/satellite/SatelliteInfo.aidl @@ -0,0 +1,19 @@ +/* + * Copyright 2024, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + package android.telephony.satellite; + + parcelable SatelliteInfo;
\ No newline at end of file diff --git a/telephony/java/android/telephony/satellite/SatelliteInfo.java b/telephony/java/android/telephony/satellite/SatelliteInfo.java new file mode 100644 index 000000000000..bca907e49993 --- /dev/null +++ b/telephony/java/android/telephony/satellite/SatelliteInfo.java @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.telephony.satellite; + +import android.annotation.FlaggedApi; +import android.os.Parcel; +import android.os.ParcelUuid; +import android.os.Parcelable; + +import androidx.annotation.NonNull; + +import com.android.internal.telephony.flags.Flags; + +import java.util.List; +import java.util.UUID; + +/** + * SatelliteInfo stores a satellite's identification, position, and frequency information + * facilitating efficient satellite communications. + * + * @hide + */ +@FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN) +public class SatelliteInfo implements Parcelable { + /** + * Unique identification number for the satellite. + * This ID is used to distinguish between different satellites in the network. + */ + @NonNull + private UUID mId; + + /** + * Position information of a satellite. + * This includes the longitude and altitude of the satellite. + */ + private SatellitePosition mPosition; + + /** + * The frequency bands to scan. Bands and earfcns won't overlap. + * Bands will be filled only if the whole band is needed. + * Maximum length of the vector is 8. + */ + private int[] mBands; + + /** + * EARFCN (E-UTRA Absolute Radio Frequency Channel Number) Ranges + * The supported frequency range list. + * Maximum length of the vector is 8. + */ + private final List<EarfcnRange> mEarfcnRangeList; + + protected SatelliteInfo(Parcel in) { + ParcelUuid parcelUuid = in.readParcelable( + ParcelUuid.class.getClassLoader(), ParcelUuid.class); + if (parcelUuid != null) { + mId = parcelUuid.getUuid(); + } + mPosition = in.readParcelable(SatellitePosition.class.getClassLoader(), + SatellitePosition.class); + int numBands = in.readInt(); + mBands = new int[numBands]; + if (numBands > 0) { + for (int i = 0; i < numBands; i++) { + mBands[i] = in.readInt(); + } + } + mEarfcnRangeList = in.createTypedArrayList(EarfcnRange.CREATOR); + } + + /** + * Constructor for {@link SatelliteInfo}. + * + * @param satelliteId The ID of the satellite. + * @param satellitePosition The {@link SatellitePosition} of the satellite. + * @param bands The list of frequency bands supported by the satellite. + * @param earfcnRanges The list of {@link EarfcnRange} objects representing the EARFCN + * ranges supported by the satellite. + */ + public SatelliteInfo(@NonNull UUID satelliteId, @NonNull SatellitePosition satellitePosition, + @NonNull int[] bands, @NonNull List<EarfcnRange> earfcnRanges) { + mId = satelliteId; + mPosition = satellitePosition; + mBands = bands; + mEarfcnRangeList = earfcnRanges; + } + + public static final Creator<SatelliteInfo> CREATOR = new Creator<SatelliteInfo>() { + @Override + public SatelliteInfo createFromParcel(Parcel in) { + return new SatelliteInfo(in); + } + + @Override + public SatelliteInfo[] newArray(int size) { + return new SatelliteInfo[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeParcelable(new ParcelUuid(mId), flags); + dest.writeParcelable(mPosition, flags); + if (mBands != null && mBands.length > 0) { + dest.writeInt(mBands.length); + dest.writeIntArray(mBands); + } else { + dest.writeInt(0); + } + dest.writeTypedList(mEarfcnRangeList); + } + + /** + * Returns the ID of the satellite. + * + * @return The satellite ID. + */ + @NonNull + public UUID getSatelliteId() { + return mId; + } + + /** + * Returns the position of the satellite. + * + * @return The {@link SatellitePosition} of the satellite. + */ + public SatellitePosition getSatellitePosition() { + return mPosition; + } + + /** + * Returns the list of frequency bands supported by the satellite. + * + * @return The list of frequency bands. + */ + @NonNull + public int[] getBands() { + return mBands; + } + + /** + * Returns the list of EARFCN ranges supported by the satellite. + * + * @return The list of {@link EarfcnRange} objects. + */ + @NonNull + public List<EarfcnRange> getEarfcnRanges() { + return mEarfcnRangeList; + } +} diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java index 7be3f337e43a..887b79883710 100644 --- a/telephony/java/android/telephony/satellite/SatelliteManager.java +++ b/telephony/java/android/telephony/satellite/SatelliteManager.java @@ -36,6 +36,7 @@ import android.os.ICancellationSignal; import android.os.OutcomeReceiver; import android.os.RemoteException; import android.os.ResultReceiver; +import android.telephony.CarrierConfigManager; import android.telephony.SubscriptionManager; import android.telephony.TelephonyCallback; import android.telephony.TelephonyFrameworkInitializer; @@ -272,6 +273,14 @@ public final class SatelliteManager { public static final String KEY_DEPROVISION_SATELLITE_TOKENS = "deprovision_satellite"; /** + * Bundle key to get the response from + * {@link #requestSatelliteAccessConfigurationForCurrentLocation(Executor, OutcomeReceiver)}. + * @hide + */ + public static final String KEY_SATELLITE_ACCESS_CONFIGURATION = + "satellite_access_configuration"; + + /** * The request was successfully processed. * @hide */ @@ -483,43 +492,43 @@ public final class SatelliteManager { /** * Telephony framework needs to access the current location of the device to perform the * request. However, location in the settings is disabled by users. - * * @hide */ - @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) + @SystemApi + @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS) public static final int SATELLITE_RESULT_LOCATION_DISABLED = 25; /** * Telephony framework needs to access the current location of the device to perform the * request. However, Telephony fails to fetch the current location from location service. - * * @hide */ - @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) + @SystemApi + @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS) public static final int SATELLITE_RESULT_LOCATION_NOT_AVAILABLE = 26; /** * Emergency call is in progress. - * * @hide */ - @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) + @SystemApi + @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS) public static final int SATELLITE_RESULT_EMERGENCY_CALL_IN_PROGRESS = 27; /** * Disabling satellite is in progress. - * * @hide */ - @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) + @SystemApi + @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS) public static final int SATELLITE_RESULT_DISABLE_IN_PROGRESS = 28; /** * Enabling satellite is in progress. - * * @hide */ - @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG) + @SystemApi + @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS) public static final int SATELLITE_RESULT_ENABLE_IN_PROGRESS = 29; /** @hide */ @@ -715,7 +724,7 @@ public final class SatelliteManager { public static final int EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_T911 = 2; /** - * This intent will be broadcasted if there are any change to list of subscriber informations. + * This intent will be broadcasted if there are any change to list of subscriber information. * This intent will be sent only to the app with component defined in * config_satellite_carrier_roaming_esos_provisioned_class and package defined in * config_satellite_gateway_service_package @@ -1349,12 +1358,16 @@ public final class SatelliteManager { * The satellite modem is being powered on. * @hide */ + @SystemApi + @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS) public static final int SATELLITE_MODEM_STATE_ENABLING_SATELLITE = 8; /** * The satellite modem is being powered off. * @hide */ + @SystemApi + @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS) public static final int SATELLITE_MODEM_STATE_DISABLING_SATELLITE = 9; /** @@ -1414,6 +1427,8 @@ public final class SatelliteManager { * there is any incoming message. * @hide */ + @SystemApi + @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS) public static final int DATAGRAM_TYPE_KEEP_ALIVE = 3; /** @@ -1421,6 +1436,8 @@ public final class SatelliteManager { * is the last message to emergency service provider indicating still needs help. * @hide */ + @SystemApi + @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS) public static final int DATAGRAM_TYPE_LAST_SOS_MESSAGE_STILL_NEED_HELP = 4; /** @@ -1428,12 +1445,16 @@ public final class SatelliteManager { * is the last message to emergency service provider indicating no more help is needed. * @hide */ + @SystemApi + @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS) public static final int DATAGRAM_TYPE_LAST_SOS_MESSAGE_NO_HELP_NEEDED = 5; /** * Datagram type indicating that the message to be sent or received is of type SMS. * @hide */ + @SystemApi + @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS) public static final int DATAGRAM_TYPE_SMS = 6; /** @@ -1441,6 +1462,8 @@ public final class SatelliteManager { * for pending incoming SMS. * @hide */ + @SystemApi + @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS) public static final int DATAGRAM_TYPE_CHECK_PENDING_INCOMING_SMS = 7; /** @hide */ @@ -1461,6 +1484,8 @@ public final class SatelliteManager { * Satellite communication restricted by user. * @hide */ + @SystemApi + @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS) public static final int SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER = 0; /** @@ -2316,6 +2341,68 @@ public final class SatelliteManager { } /** + * Request to get satellite access configuration for the current location. + * + * @param executor The executor on which the callback will be called. + * @param callback The callback object to which the result will be delivered. + * If the request is successful, {@link OutcomeReceiver#onResult(Object)} + * will return a {@code SatelliteAccessConfiguration} with value the regional + * satellite access configuration at the current location. + * If the request is not successful, {@link OutcomeReceiver#onError(Throwable)} + * will return a {@link SatelliteException} with the {@link SatelliteResult}. + * + * @throws SecurityException if the caller doesn't have required permission. + * + * @hide + */ + @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION) + @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN) + public void requestSatelliteAccessConfigurationForCurrentLocation( + @NonNull @CallbackExecutor Executor executor, + @NonNull OutcomeReceiver<SatelliteAccessConfiguration, SatelliteException> callback) { + Objects.requireNonNull(executor); + Objects.requireNonNull(callback); + + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + ResultReceiver receiver = new ResultReceiver(null) { + @Override + protected void onReceiveResult(int resultCode, Bundle resultData) { + if (resultCode == SATELLITE_RESULT_SUCCESS) { + if (resultData.containsKey(KEY_SATELLITE_ACCESS_CONFIGURATION)) { + SatelliteAccessConfiguration satelliteAccessConfiguration = + resultData.getParcelable(KEY_SATELLITE_ACCESS_CONFIGURATION, + SatelliteAccessConfiguration.class); + executor.execute(() -> Binder.withCleanCallingIdentity(() -> + callback.onResult(satelliteAccessConfiguration))); + } else { + loge("KEY_SATELLITE_ACCESS_CONFIGURATION does not exist."); + executor.execute(() -> Binder.withCleanCallingIdentity(() -> + callback.onError(new SatelliteException( + SATELLITE_RESULT_REQUEST_FAILED)))); + } + } else { + executor.execute(() -> Binder.withCleanCallingIdentity(() -> + callback.onError(new SatelliteException(resultCode)))); + } + } + }; + telephony.requestSatelliteAccessConfigurationForCurrentLocation(receiver); + } else { + loge("requestSatelliteAccessConfigurationForCurrentLocation() invalid telephony"); + executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError( + new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE)))); + } + } catch (RemoteException ex) { + loge("requestSatelliteAccessConfigurationForCurrentLocation() RemoteException: " + + ex); + executor.execute(() -> Binder.withCleanCallingIdentity(() -> callback.onError( + new SatelliteException(SATELLITE_RESULT_ILLEGAL_STATE)))); + } + } + + /** * Request to get the duration in seconds after which the satellite will be visible. * This will be {@link Duration#ZERO} if the satellite is currently visible. * @@ -2420,7 +2507,7 @@ public final class SatelliteManager { * <li>There is no satellite communication restriction, which is added by * {@link #addAttachRestrictionForCarrier(int, int, Executor, Consumer)}</li> * <li>The carrier config {@link - * android.telephony.CarrierConfigManager#KEY_SATELLITE_ATTACH_SUPPORTED_BOOL} is set to + * CarrierConfigManager#KEY_SATELLITE_ATTACH_SUPPORTED_BOOL} is set to * {@code true}.</li> * </ul> * @@ -2743,7 +2830,7 @@ public final class SatelliteManager { * <p> * Note: This API is specifically designed for OEM enabled satellite connectivity only. * For satellite connectivity enabled using carrier roaming, please refer to - * {@link android.telephony.TelephonyCallback.SignalStrengthsListener}, and + * {@link TelephonyCallback.SignalStrengthsListener}, and * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}. * </p> * @@ -2814,7 +2901,7 @@ public final class SatelliteManager { * <p> * Note: This API is specifically designed for OEM enabled satellite connectivity only. * For satellite connectivity enabled using carrier roaming, please refer to - * {@link android.telephony.TelephonyCallback.SignalStrengthsListener}, and + * {@link TelephonyCallback.SignalStrengthsListener}, and * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}. * </p> * @@ -3131,6 +3218,15 @@ public final class SatelliteManager { () -> callback.onSatelliteCommunicationAllowedStateChanged( isAllowed))); } + + @Override + public void onSatelliteAccessConfigurationChanged( + @Nullable SatelliteAccessConfiguration + satelliteAccessConfiguration) { + executor.execute(() -> Binder.withCleanCallingIdentity( + () -> callback.onSatelliteAccessConfigurationChanged( + satelliteAccessConfiguration))); + } }; sSatelliteCommunicationAllowedStateCallbackMap.put(callback, internalCallback); return telephony.registerForCommunicationAllowedStateChanged( @@ -3415,6 +3511,40 @@ public final class SatelliteManager { } } + /** + * Inform whether application supports NTN SMS in satellite mode. + * + * This method is used by default messaging application to inform framework whether it supports + * NTN SMS or not. + * + * Invoking this API will internally result in triggering + * {@link android.telephony.TelephonyCallback.CarrierRoamingNtnModeListener + * #onCarrierRoamingNtnAvailableServicesChanged(List)} and + * {@link android.telephony.TelephonyCallback.CarrierRoamingNtnModeListener + * #onCarrierRoamingNtnEligibleStateChanged(boolean)} callbacks. + * + * @param ntnSmsSupported {@code true} If application supports NTN SMS, else {@code false}. + * + * @throws SecurityException if the caller doesn't have required permission. + * @throws IllegalStateException if the Telephony process is not currently available. + * @hide + */ + @RequiresPermission(allOf = {Manifest.permission.SATELLITE_COMMUNICATION, + Manifest.permission.SEND_SMS}) + public void setNtnSmsSupported(boolean ntnSmsSupported) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + telephony.setNtnSmsSupported(ntnSmsSupported); + } else { + throw new IllegalStateException("telephony service is null."); + } + } catch (RemoteException ex) { + loge("setNtnSmsSupported() RemoteException:" + ex); + ex.rethrowAsRuntimeException(); + } + } + @Nullable private static ITelephony getITelephony() { ITelephony binder = ITelephony.Stub.asInterface(TelephonyFrameworkInitializer diff --git a/telephony/java/android/telephony/satellite/SatellitePosition.aidl b/telephony/java/android/telephony/satellite/SatellitePosition.aidl new file mode 100644 index 000000000000..a8028eb48ee7 --- /dev/null +++ b/telephony/java/android/telephony/satellite/SatellitePosition.aidl @@ -0,0 +1,19 @@ +/* + * Copyright 2024, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + package android.telephony.satellite; + + parcelable SatellitePosition;
\ No newline at end of file diff --git a/telephony/java/android/telephony/satellite/SatellitePosition.java b/telephony/java/android/telephony/satellite/SatellitePosition.java new file mode 100644 index 000000000000..1e8c0180f456 --- /dev/null +++ b/telephony/java/android/telephony/satellite/SatellitePosition.java @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.telephony.satellite; + +import android.annotation.FlaggedApi; +import android.os.Parcel; +import android.os.Parcelable; + +import androidx.annotation.NonNull; + +import com.android.internal.telephony.flags.Flags; + +/** + * The position of a satellite in Earth orbit. + * + * Longitude is the angular distance, measured in degrees, east or west of the prime longitude line + * ranging from -180 to 180 degrees + * Altitude is the distance from the center of the Earth to the satellite, measured in kilometers + * + * @hide + */ +@FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN) +public class SatellitePosition implements Parcelable { + + /** + * The longitude of the satellite in degrees, ranging from -180 to 180 degrees + */ + private double mLongitudeDegree; + + /** + * The distance from the center of the earth to the satellite, measured in kilometers + */ + private double mAltitudeKm; + + /** + * Constructor for {@link SatellitePosition} used to create an instance from a {@link Parcel}. + * + * @param in The {@link Parcel} to read the satellite position data from. + */ + public SatellitePosition(Parcel in) { + mLongitudeDegree = in.readDouble(); + mAltitudeKm = in.readDouble(); + } + + /** + * Constructor for {@link SatellitePosition}. + * + * @param longitudeDegree The longitude of the satellite in degrees. + * @param altitudeKm The altitude of the satellite in kilometers. + */ + public SatellitePosition(double longitudeDegree, double altitudeKm) { + mLongitudeDegree = longitudeDegree; + mAltitudeKm = altitudeKm; + } + + public static final Creator<SatellitePosition> CREATOR = new Creator<SatellitePosition>() { + @Override + public SatellitePosition createFromParcel(Parcel in) { + return new SatellitePosition(in); + } + + @Override + public SatellitePosition[] newArray(int size) { + return new SatellitePosition[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + /** + * @param dest The Parcel in which the object should be written. + * @param flags Additional flags about how the object should be written. + * May be 0 or {@link #PARCELABLE_WRITE_RETURN_VALUE}. + */ + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeDouble(mLongitudeDegree); + dest.writeDouble(mAltitudeKm); + } + + /** + * Returns the longitude of the satellite in degrees, ranging from -180 to 180 degrees. + * + * @return The longitude of the satellite. + */ + public double getLongitudeDegrees() { + return mLongitudeDegree; + } + + /** + * Returns the altitude of the satellite in kilometers + * + * @return The altitude of the satellite. + */ + public double getAltitudeKm() { + return mAltitudeKm; + } +} diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index 62cbb02c9fc7..a58427318b55 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -886,7 +886,7 @@ interface ITelephony { /** * @return true if the ImsService to bind to for the slot id specified was set, false otherwise. */ - boolean setBoundImsServiceOverride(int slotIndex, boolean isCarrierService, + boolean setBoundImsServiceOverride(int slotIndex, int userId, boolean isCarrierService, in int[] featureTypes, in String packageName); /** @@ -2999,6 +2999,16 @@ interface ITelephony { void requestIsCommunicationAllowedForCurrentLocation(int subId, in ResultReceiver receiver); /** + * Request to get satellite access configuration for the current location. + * + * @param receiver Result receiver to get the error code of the request + * and satellite access configuration for the current location. + */ + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(" + + "android.Manifest.permission.SATELLITE_COMMUNICATION)") + void requestSatelliteAccessConfigurationForCurrentLocation(in ResultReceiver receiver); + + /** * Request to get the time after which the satellite will be visible. * * @param receiver Result receiver to get the error code of the request and the requested @@ -3487,4 +3497,15 @@ interface ITelephony { @JavaPassthrough(annotation="@android.annotation.RequiresPermission(" + "android.Manifest.permission.SATELLITE_COMMUNICATION)") void deprovisionSatellite(in List<SatelliteSubscriberInfo> list, in ResultReceiver result); + + /** + * Inform whether application supports NTN SMS in satellite mode. + * + * This method is used by default messaging application to inform framework whether it supports + * NTN SMS or not. + * + * @param ntnSmsSupported {@code true} If application supports NTN SMS, else {@code false}. + * @hide + */ + void setNtnSmsSupported(boolean ntnSmsSupported); } diff --git a/tests/AppJankTest/AndroidManifest.xml b/tests/AppJankTest/AndroidManifest.xml index ae973393b90e..abed1798c47c 100644 --- a/tests/AppJankTest/AndroidManifest.xml +++ b/tests/AppJankTest/AndroidManifest.xml @@ -18,7 +18,7 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android.app.jank.tests"> - <application> + <application android:appCategory="news"> <uses-library android:name="android.test.runner" /> <activity android:name=".EmptyActivity" android:label="EmptyActivity" diff --git a/tests/AppJankTest/src/android/app/jank/tests/JankTrackerTest.java b/tests/AppJankTest/src/android/app/jank/tests/JankTrackerTest.java index a3e5533599bc..1bdf019d6c42 100644 --- a/tests/AppJankTest/src/android/app/jank/tests/JankTrackerTest.java +++ b/tests/AppJankTest/src/android/app/jank/tests/JankTrackerTest.java @@ -17,6 +17,7 @@ package android.app.jank.tests; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; import android.app.jank.Flags; import android.app.jank.JankTracker; @@ -31,6 +32,8 @@ import androidx.test.annotation.UiThreadTest; import androidx.test.core.app.ActivityScenario; import androidx.test.runner.AndroidJUnit4; +import com.android.internal.policy.DecorView; + import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; @@ -153,4 +156,16 @@ public class JankTrackerTest { assertEquals(1, stateData.size()); } + + /** + * Test confirms a JankTracker object is retrieved from the activity. + */ + @Test + @RequiresFlagsEnabled(Flags.FLAG_DETAILED_APP_JANK_METRICS_LOGGING_ENABLED) + public void jankTracker_NotNull_WhenRetrievedFromDecorView() { + DecorView decorView = (DecorView) sActivityDecorView; + JankTracker jankTracker = decorView.getJankTracker(); + + assertNotNull(jankTracker); + } } 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 332b9b832037..ea61ad9d4481 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 @@ -21,13 +21,14 @@ import android.graphics.Insets import android.graphics.Rect import android.graphics.Region import android.os.SystemClock -import android.platform.uiautomator_helpers.DeviceHelpers +import android.platform.uiautomatorhelpers.DeviceHelpers import android.tools.device.apphelpers.IStandardAppHelper import android.tools.helpers.SYSTEMUI_PACKAGE import android.tools.traces.parsers.WindowManagerStateHelper import android.tools.traces.wm.WindowingMode import android.view.WindowInsets import android.view.WindowManager +import android.window.DesktopModeFlags import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation import androidx.test.uiautomator.By import androidx.test.uiautomator.BySelector @@ -35,7 +36,6 @@ import androidx.test.uiautomator.UiDevice import androidx.test.uiautomator.UiObject2 import androidx.test.uiautomator.Until import com.android.server.wm.flicker.helpers.MotionEventHelper.InputMethod.TOUCH -import com.android.window.flags.Flags import java.time.Duration /** @@ -107,7 +107,7 @@ open class DesktopModeAppHelper(private val innerHelper: IStandardAppHelper) : // drag the window to move to desktop if (motionEventHelper.inputMethod == TOUCH - && Flags.enableHoldToDragAppHandle()) { + && DesktopModeFlags.ENABLE_HOLD_TO_DRAG_APP_HANDLE.isTrue) { // Touch requires hold-to-drag. motionEventHelper.holdToDrag(startX, startY, startX, endY, steps = 100) } else { @@ -159,7 +159,7 @@ open class DesktopModeAppHelper(private val innerHelper: IStandardAppHelper) : ) { val caption = getCaptionForTheApp(wmHelper, device) val maximizeButton = getMaximizeButtonForTheApp(caption) - maximizeButton?.longClick() + maximizeButton.longClick() wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify() val buttonResId = if (toLeft) SNAP_LEFT_BUTTON else SNAP_RIGHT_BUTTON diff --git a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt index 1574d1b7ce6f..09a686ca2c3f 100644 --- a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt +++ b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt @@ -757,7 +757,55 @@ class KeyGestureControllerTests { intArrayOf(KeyEvent.KEYCODE_MINUS), KeyEvent.META_ALT_ON, intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) - ) + ), + TestData( + "META + ALT + '-' -> Magnifier Zoom Out", + intArrayOf( + KeyEvent.KEYCODE_META_LEFT, + KeyEvent.KEYCODE_ALT_LEFT, + KeyEvent.KEYCODE_MINUS + ), + KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_OUT, + intArrayOf(KeyEvent.KEYCODE_MINUS), + KeyEvent.META_META_ON or KeyEvent.META_ALT_ON, + intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) + ), + TestData( + "META + ALT + '=' -> Magnifier Zoom In", + intArrayOf( + KeyEvent.KEYCODE_META_LEFT, + KeyEvent.KEYCODE_ALT_LEFT, + KeyEvent.KEYCODE_EQUALS + ), + KeyGestureEvent.KEY_GESTURE_TYPE_MAGNIFIER_ZOOM_IN, + intArrayOf(KeyEvent.KEYCODE_EQUALS), + KeyEvent.META_META_ON or KeyEvent.META_ALT_ON, + intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) + ), + TestData( + "META + ALT + M -> Toggle Magnification", + intArrayOf( + KeyEvent.KEYCODE_META_LEFT, + KeyEvent.KEYCODE_ALT_LEFT, + KeyEvent.KEYCODE_M + ), + KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION, + intArrayOf(KeyEvent.KEYCODE_M), + KeyEvent.META_META_ON or KeyEvent.META_ALT_ON, + intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) + ), + TestData( + "META + ALT + S -> Activate Select to Speak", + intArrayOf( + KeyEvent.KEYCODE_META_LEFT, + KeyEvent.KEYCODE_ALT_LEFT, + KeyEvent.KEYCODE_S + ), + KeyGestureEvent.KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK, + intArrayOf(KeyEvent.KEYCODE_S), + KeyEvent.META_META_ON or KeyEvent.META_ALT_ON, + intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) + ), ) } @@ -770,6 +818,7 @@ class KeyGestureControllerTests { com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_SLOW_KEYS_FLAG, com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_STICKY_KEYS_FLAG, com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_MOUSE_KEYS, + com.android.hardware.input.Flags.FLAG_ENABLE_TALKBACK_AND_MAGNIFIER_KEY_GESTURES, com.android.window.flags.Flags.FLAG_ENABLE_MOVE_TO_NEXT_DISPLAY_SHORTCUT, com.android.window.flags.Flags.FLAG_ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS ) @@ -787,6 +836,7 @@ class KeyGestureControllerTests { com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_SLOW_KEYS_FLAG, com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_STICKY_KEYS_FLAG, com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_MOUSE_KEYS, + com.android.hardware.input.Flags.FLAG_ENABLE_TALKBACK_AND_MAGNIFIER_KEY_GESTURES, com.android.window.flags.Flags.FLAG_ENABLE_MOVE_TO_NEXT_DISPLAY_SHORTCUT, com.android.window.flags.Flags.FLAG_ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS ) @@ -995,11 +1045,28 @@ class KeyGestureControllerTests { intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE), AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_CALCULATOR) ), + TestData( + "LOCK -> Lock Screen", + intArrayOf(KeyEvent.KEYCODE_LOCK), + KeyGestureEvent.KEY_GESTURE_TYPE_LOCK_SCREEN, + intArrayOf(KeyEvent.KEYCODE_LOCK), + 0, + intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) + ), + TestData( + "FULLSCREEN -> Maximizes a task to fit the screen", + intArrayOf(KeyEvent.KEYCODE_FULLSCREEN), + KeyGestureEvent.KEY_GESTURE_TYPE_MAXIMIZE_FREEFORM_WINDOW, + intArrayOf(KeyEvent.KEYCODE_FULLSCREEN), + 0, + intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE) + ), ) } @Test @Parameters(method = "systemKeysTestArguments") + @EnableFlags(com.android.hardware.input.Flags.FLAG_ENABLE_NEW_25Q2_KEYCODES) fun testSystemKeys(test: TestData) { setupKeyGestureController() testKeyGestureInternal(test) @@ -1029,6 +1096,9 @@ class KeyGestureControllerTests { KeyEvent.KEYCODE_STYLUS_BUTTON_SECONDARY, KeyEvent.KEYCODE_STYLUS_BUTTON_TERTIARY, KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL, + KeyEvent.KEYCODE_DO_NOT_DISTURB, + KeyEvent.KEYCODE_LOCK, + KeyEvent.KEYCODE_FULLSCREEN ) val handler = KeyGestureHandler { _, _ -> false } diff --git a/tests/Input/src/com/android/server/input/KeyboardBacklightControllerTests.kt b/tests/Input/src/com/android/server/input/KeyboardBacklightControllerTests.kt index 58fb4e1ed103..938e2f8a3611 100644 --- a/tests/Input/src/com/android/server/input/KeyboardBacklightControllerTests.kt +++ b/tests/Input/src/com/android/server/input/KeyboardBacklightControllerTests.kt @@ -19,6 +19,7 @@ package com.android.server.input import android.animation.ValueAnimator import android.content.Context import android.content.ContextWrapper +import android.content.res.Resources import android.graphics.Color import android.hardware.input.IKeyboardBacklightListener import android.hardware.input.IKeyboardBacklightState @@ -28,11 +29,12 @@ import android.os.UEventObserver import android.os.test.TestLooper import android.platform.test.annotations.Presubmit import android.view.InputDevice +import android.util.TypedValue import androidx.test.annotation.UiThreadTest import androidx.test.core.app.ApplicationProvider +import com.android.internal.R import com.android.server.input.KeyboardBacklightController.DEFAULT_BRIGHTNESS_VALUE_FOR_LEVEL import com.android.server.input.KeyboardBacklightController.MAX_BRIGHTNESS_CHANGE_STEPS -import com.android.server.input.KeyboardBacklightController.USER_INACTIVITY_THRESHOLD_MILLIS import com.android.test.input.MockInputManagerRule import java.io.FileNotFoundException import java.io.FileOutputStream @@ -49,6 +51,7 @@ import org.junit.Rule import org.junit.Test import org.mockito.Mock import org.mockito.Mockito.any +import org.mockito.Mockito.anyBoolean import org.mockito.Mockito.anyInt import org.mockito.Mockito.eq import org.mockito.Mockito.spy @@ -94,6 +97,7 @@ class KeyboardBacklightControllerTests { const val LIGHT_ID = 2 const val SECOND_LIGHT_ID = 3 const val MAX_BRIGHTNESS = 255 + const val USER_INACTIVITY_THRESHOLD_MILLIS = 30000 } @get:Rule @@ -105,6 +109,8 @@ class KeyboardBacklightControllerTests { private lateinit var native: NativeInputManagerService @Mock private lateinit var uEventManager: UEventManager + @Mock + private lateinit var resources: Resources private lateinit var keyboardBacklightController: KeyboardBacklightController private lateinit var context: Context private lateinit var dataStore: PersistentDataStore @@ -117,6 +123,7 @@ class KeyboardBacklightControllerTests { @Before fun setup() { context = spy(ContextWrapper(ApplicationProvider.getApplicationContext())) + `when`(context.resources).thenReturn(resources) dataStore = PersistentDataStore(object : PersistentDataStore.Injector() { override fun openRead(): InputStream? { throw FileNotFoundException() @@ -129,6 +136,7 @@ class KeyboardBacklightControllerTests { override fun finishWrite(fos: FileOutputStream?, success: Boolean) {} }) testLooper = TestLooper() + setupConfig() keyboardBacklightController = KeyboardBacklightController(context, native, dataStore, testLooper.looper, FakeAnimatorFactory(), uEventManager) val inputManager = InputManager(context) @@ -147,7 +155,31 @@ class KeyboardBacklightControllerTests { sysfsNodeChanges++ } } - + private fun setupConfig() { + val brightnessValues = intArrayOf(100, 200, 0) + val decreaseThresholds = intArrayOf(-1, 900, 1900) + val increaseThresholds = intArrayOf(1000, 2000, -1) + `when`(resources.getIntArray(R.array.config_autoKeyboardBacklightBrightnessValues)) + .thenReturn(brightnessValues) + `when`(resources.getIntArray(R.array.config_autoKeyboardBacklightDecreaseLuxThreshold)) + .thenReturn(decreaseThresholds) + `when`(resources.getIntArray(R.array.config_autoKeyboardBacklightIncreaseLuxThreshold)) + .thenReturn(increaseThresholds) + `when`(resources.getInteger(R.integer.config_keyboardBacklightTimeoutMs)) + .thenReturn(USER_INACTIVITY_THRESHOLD_MILLIS) + `when`( + resources.getValue( + eq(R.dimen.config_autoKeyboardBrightnessSmoothingConstant), + any(TypedValue::class.java), + anyBoolean() + ) + ).then { + val args = it.arguments + val outValue = args[1] as TypedValue + outValue.data = java.lang.Float.floatToRawIntBits(1.0f) + Unit + } + } @Test fun testKeyboardBacklightIncrementDecrement() { KeyboardBacklightFlags( @@ -365,7 +397,7 @@ class KeyboardBacklightControllerTests { lightColorMap[LIGHT_ID] ) - testLooper.moveTimeForward(USER_INACTIVITY_THRESHOLD_MILLIS + 1000) + testLooper.moveTimeForward((USER_INACTIVITY_THRESHOLD_MILLIS + 1000).toLong()) testLooper.dispatchNext() assertEquals( "Keyboard backlight level should be turned off after inactivity", diff --git a/tests/UsbManagerTests/lib/src/com/android/server/usblib/UsbManagerTestLib.java b/tests/UsbManagerTests/lib/src/com/android/server/usblib/UsbManagerTestLib.java index e2099e652c49..635e5de935c7 100644 --- a/tests/UsbManagerTests/lib/src/com/android/server/usblib/UsbManagerTestLib.java +++ b/tests/UsbManagerTests/lib/src/com/android/server/usblib/UsbManagerTestLib.java @@ -18,19 +18,27 @@ package com.android.server.usblib; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThrows; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.Context; +import android.hardware.usb.UsbAccessory; import android.hardware.usb.UsbManager; +import android.hardware.usb.flags.Flags; import android.os.Binder; +import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.util.Log; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.io.FileDescriptor; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.util.concurrent.atomic.AtomicInteger; /** @@ -43,13 +51,36 @@ public class UsbManagerTestLib { private UsbManager mUsbManagerSys; private UsbManager mUsbManagerMock; - @Mock private android.hardware.usb.IUsbManager mMockUsbService; + @Mock + private android.hardware.usb.IUsbManager mMockUsbService; + private TestParcelFileDescriptor mTestParcelFileDescriptor = new TestParcelFileDescriptor( + new ParcelFileDescriptor(new FileDescriptor())); + @Mock + private UsbAccessory mMockUsbAccessory; /** * Counter for tracking UsbOperation operations. */ private static final AtomicInteger sUsbOperationCount = new AtomicInteger(); + private class TestParcelFileDescriptor extends ParcelFileDescriptor { + + private final AtomicInteger mCloseCount = new AtomicInteger(); + + TestParcelFileDescriptor(ParcelFileDescriptor wrapped) { + super(wrapped); + } + + @Override + public void close() { + int unused = mCloseCount.incrementAndGet(); + } + + public void clearCloseCount() { + mCloseCount.set(0); + } + } + public UsbManagerTestLib(Context context) { MockitoAnnotations.initMocks(this); mContext = context; @@ -74,6 +105,34 @@ public class UsbManagerTestLib { mUsbManagerSys.setCurrentFunctions(functions); } + private InputStream openAccessoryInputStream(UsbAccessory accessory) { + try { + when(mMockUsbService.openAccessory(accessory)).thenReturn(mTestParcelFileDescriptor); + } catch (RemoteException remEx) { + Log.w(TAG, "RemoteException"); + } + + if (Flags.enableAccessoryStreamApi()) { + return mUsbManagerMock.openAccessoryInputStream(accessory); + } + + throw new UnsupportedOperationException("Stream APIs not available"); + } + + private OutputStream openAccessoryOutputStream(UsbAccessory accessory) { + try { + when(mMockUsbService.openAccessory(accessory)).thenReturn(mTestParcelFileDescriptor); + } catch (RemoteException remEx) { + Log.w(TAG, "RemoteException"); + } + + if (Flags.enableAccessoryStreamApi()) { + return mUsbManagerMock.openAccessoryOutputStream(accessory); + } + + throw new UnsupportedOperationException("Stream APIs not available"); + } + private void testSetGetCurrentFunctions_Matched(long functions) { setCurrentFunctions(functions); assertEquals("CurrentFunctions mismatched: ", functions, getCurrentFunctions()); @@ -94,7 +153,7 @@ public class UsbManagerTestLib { try { setCurrentFunctions(functions); - verify(mMockUsbService).setCurrentFunctions(eq(functions), operationId); + verify(mMockUsbService).setCurrentFunctions(eq(functions), eq(operationId)); } catch (RemoteException remEx) { Log.w(TAG, "RemoteException"); } @@ -118,7 +177,7 @@ public class UsbManagerTestLib { int operationId = sUsbOperationCount.incrementAndGet() + Binder.getCallingUid(); setCurrentFunctions(functions); - verify(mMockUsbService).setCurrentFunctions(eq(functions), operationId); + verify(mMockUsbService).setCurrentFunctions(eq(functions), eq(operationId)); } public void testGetCurrentFunctions_shouldMatched() { @@ -138,4 +197,47 @@ public class UsbManagerTestLib { testSetCurrentFunctionsMock_Matched(UsbManager.FUNCTION_RNDIS); testSetCurrentFunctionsMock_Matched(UsbManager.FUNCTION_NCM); } + + public void testParcelFileDescriptorClosedWhenAllOpenStreamsAreClosed() { + mTestParcelFileDescriptor.clearCloseCount(); + try { + try (InputStream ignored = openAccessoryInputStream(mMockUsbAccessory)) { + //noinspection EmptyTryBlock + try (OutputStream ignored2 = openAccessoryOutputStream(mMockUsbAccessory)) { + // do nothing + } + } + + // ParcelFileDescriptor is closed only once. + assertEquals(mTestParcelFileDescriptor.mCloseCount.get(), 1); + mTestParcelFileDescriptor.clearCloseCount(); + } catch (IOException e) { + // do nothing + } + } + + public void testOnlyOneOpenInputStreamAllowed() { + try { + //noinspection EmptyTryBlock + try (InputStream ignored = openAccessoryInputStream(mMockUsbAccessory)) { + assertThrows(IllegalStateException.class, + () -> openAccessoryInputStream(mMockUsbAccessory)); + } + } catch (IOException e) { + // do nothing + } + } + + public void testOnlyOneOpenOutputStreamAllowed() { + try { + //noinspection EmptyTryBlock + try (OutputStream ignored = openAccessoryOutputStream(mMockUsbAccessory)) { + assertThrows(IllegalStateException.class, + () -> openAccessoryOutputStream(mMockUsbAccessory)); + } + } catch (IOException e) { + // do nothing + } + } + } diff --git a/tests/UsbManagerTests/src/com/android/server/usbtest/UsbManagerApiTest.java b/tests/UsbManagerTests/src/com/android/server/usbtest/UsbManagerApiTest.java index 8b21763b4a24..40fd0b431451 100644 --- a/tests/UsbManagerTests/src/com/android/server/usbtest/UsbManagerApiTest.java +++ b/tests/UsbManagerTests/src/com/android/server/usbtest/UsbManagerApiTest.java @@ -18,17 +18,21 @@ package com.android.server.usbtest; import android.content.Context; import android.hardware.usb.UsbManager; +import android.hardware.usb.flags.Flags; +import android.platform.test.annotations.EnableFlags; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; -import org.junit.Ignore; +import com.android.server.usblib.UsbManagerTestLib; + +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; -import com.android.server.usblib.UsbManagerTestLib; - /** * Unit tests for {@link android.hardware.usb.UsbManager}. * Note: MUST claimed MANAGE_USB permission in Manifest @@ -41,6 +45,9 @@ public class UsbManagerApiTest { private final UsbManagerTestLib mUsbManagerTestLib = new UsbManagerTestLib(mContext = InstrumentationRegistry.getContext()); + @Rule + public final CheckFlagsRule mCheckFlagsRule = + DeviceFlagsValueProvider.createCheckFlagsRule(); /** * Verify NO SecurityException * Go through System Server @@ -92,4 +99,23 @@ public class UsbManagerApiTest { public void testUsbApi_SetCurrentFunctions_shouldMatched() { mUsbManagerTestLib.testSetCurrentFunctions_shouldMatched(); } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_ACCESSORY_STREAM_API) + public void testUsbApi_closesParcelFileDescriptorAfterAllStreamsClosed() { + mUsbManagerTestLib.testParcelFileDescriptorClosedWhenAllOpenStreamsAreClosed(); + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_ACCESSORY_STREAM_API) + public void testUsbApi_callingOpenAccessoryInputStreamTwiceThrowsException() { + mUsbManagerTestLib.testOnlyOneOpenInputStreamAllowed(); + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_ACCESSORY_STREAM_API) + public void testUsbApi_callingOpenAccessoryOutputStreamTwiceThrowsException() { + mUsbManagerTestLib.testOnlyOneOpenOutputStreamAllowed(); + } + } diff --git a/tests/testables/src/android/testing/TestableLooper.java b/tests/testables/src/android/testing/TestableLooper.java index ac96ef28f501..be5c84c0353c 100644 --- a/tests/testables/src/android/testing/TestableLooper.java +++ b/tests/testables/src/android/testing/TestableLooper.java @@ -53,7 +53,6 @@ public class TestableLooper { private static final Field MESSAGE_QUEUE_MESSAGES_FIELD; private static final Field MESSAGE_NEXT_FIELD; private static final Field MESSAGE_WHEN_FIELD; - private static Field MESSAGE_QUEUE_USE_CONCURRENT_FIELD = null; private Looper mLooper; private MessageQueue mQueue; @@ -64,14 +63,6 @@ public class TestableLooper { static { try { - MESSAGE_QUEUE_USE_CONCURRENT_FIELD = - MessageQueue.class.getDeclaredField("mUseConcurrent"); - MESSAGE_QUEUE_USE_CONCURRENT_FIELD.setAccessible(true); - } catch (NoSuchFieldException ignored) { - // Ignore - maybe this is not CombinedMessageQueue? - } - - try { MESSAGE_QUEUE_MESSAGES_FIELD = MessageQueue.class.getDeclaredField("mMessages"); MESSAGE_QUEUE_MESSAGES_FIELD.setAccessible(true); MESSAGE_NEXT_FIELD = Message.class.getDeclaredField("next"); @@ -155,15 +146,6 @@ public class TestableLooper { mLooper = l; mQueue = mLooper.getQueue(); mHandler = new Handler(mLooper); - - // If we are using CombinedMessageQueue, we need to disable concurrent mode for testing. - if (MESSAGE_QUEUE_USE_CONCURRENT_FIELD != null) { - try { - MESSAGE_QUEUE_USE_CONCURRENT_FIELD.set(mQueue, false); - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } - } } /** diff --git a/tests/utils/testutils/java/android/os/test/TestLooper.java b/tests/utils/testutils/java/android/os/test/TestLooper.java index 1bcfaf60857d..56b0a25ed2dd 100644 --- a/tests/utils/testutils/java/android/os/test/TestLooper.java +++ b/tests/utils/testutils/java/android/os/test/TestLooper.java @@ -100,18 +100,6 @@ public class TestLooper { throw new RuntimeException("Reflection error constructing or accessing looper", e); } - // If we are using CombinedMessageQueue, we need to disable concurrent mode for testing. - try { - Field messageQueueUseConcurrentField = - MessageQueue.class.getDeclaredField("mUseConcurrent"); - messageQueueUseConcurrentField.setAccessible(true); - messageQueueUseConcurrentField.set(mLooper.getQueue(), false); - } catch (NoSuchFieldException e) { - // Ignore - maybe this is not CombinedMessageQueue? - } catch (IllegalAccessException e) { - throw new RuntimeException("Reflection error constructing or accessing looper", e); - } - mClock = clock; } diff --git a/tools/aapt2/Debug.cpp b/tools/aapt2/Debug.cpp index 2527dcd26382..661df4d0fe33 100644 --- a/tools/aapt2/Debug.cpp +++ b/tools/aapt2/Debug.cpp @@ -21,10 +21,13 @@ #include <format/binary/ResChunkPullParser.h> #include <algorithm> +#include <array> #include <map> #include <memory> #include <queue> #include <set> +#include <span> +#include <utility> #include <vector> #include "ResourceTable.h" @@ -684,6 +687,80 @@ class ChunkPrinter { printer_->Print("\n"); } + void PrintQualifiers(uint32_t qualifiers) const { + if (qualifiers == 0) { + printer_->Print("0"); + return; + } + + printer_->Print(StringPrintf("0x%04x: ", qualifiers)); + static constinit std::array kValues = { + std::pair{ResTable_config::CONFIG_MCC, "mcc"}, + std::pair{ResTable_config::CONFIG_MNC, "mnc"}, + std::pair{ResTable_config::CONFIG_LOCALE, "locale"}, + std::pair{ResTable_config::CONFIG_TOUCHSCREEN, "touchscreen"}, + std::pair{ResTable_config::CONFIG_KEYBOARD, "keyboard"}, + std::pair{ResTable_config::CONFIG_KEYBOARD_HIDDEN, "keyboard_hidden"}, + std::pair{ResTable_config::CONFIG_NAVIGATION, "navigation"}, + std::pair{ResTable_config::CONFIG_ORIENTATION, "orientation"}, + std::pair{ResTable_config::CONFIG_DENSITY, "screen_density"}, + std::pair{ResTable_config::CONFIG_SCREEN_SIZE, "screen_size"}, + std::pair{ResTable_config::CONFIG_SMALLEST_SCREEN_SIZE, "screen_smallest_size"}, + std::pair{ResTable_config::CONFIG_VERSION, "version"}, + std::pair{ResTable_config::CONFIG_SCREEN_LAYOUT, "screen_layout"}, + std::pair{ResTable_config::CONFIG_UI_MODE, "ui_mode"}, + std::pair{ResTable_config::CONFIG_LAYOUTDIR, "layout_dir"}, + std::pair{ResTable_config::CONFIG_SCREEN_ROUND, "screen_round"}, + std::pair{ResTable_config::CONFIG_COLOR_MODE, "color_mode"}, + std::pair{ResTable_config::CONFIG_GRAMMATICAL_GENDER, "grammatical_gender"}}; + const char* delimiter = ""; + for (auto&& pair : kValues) { + if (qualifiers & pair.first) { + printer_->Print(StringPrintf("%s%s", delimiter, pair.second)); + delimiter = "|"; + } + } + } + + bool PrintTypeSpec(const ResTable_typeSpec* chunk) const { + printer_->Print(StringPrintf(" id: 0x%02x", android::util::DeviceToHost32(chunk->id))); + printer_->Print(StringPrintf(" types: %u", android::util::DeviceToHost16(chunk->typesCount))); + printer_->Print( + StringPrintf(" entry configs: %u\n", android::util::DeviceToHost32(chunk->entryCount))); + printer_->Print("Entry qualifier masks:\n"); + printer_->Indent(); + std::span<const uint32_t> masks(reinterpret_cast<const uint32_t*>(GetChunkData(&chunk->header)), + GetChunkDataLen(&chunk->header) / sizeof(uint32_t)); + int i = 0; + int non_empty_count = 0; + for (auto dev_mask : masks) { + auto mask = android::util::DeviceToHost32(dev_mask); + if (mask == 0) { + i++; + continue; + } + ++non_empty_count; + printer_->Print(StringPrintf("#0x%02x = ", i++)); + if (mask & ResTable_typeSpec::SPEC_PUBLIC) { + mask &= ~ResTable_typeSpec::SPEC_PUBLIC; + printer_->Print("(PUBLIC) "); + } + if (mask & ResTable_typeSpec::SPEC_STAGED_API) { + mask &= ~ResTable_typeSpec::SPEC_STAGED_API; + printer_->Print("(STAGED) "); + } + PrintQualifiers(mask); + printer_->Print("\n"); + } + if (non_empty_count > 0) { + printer_->Print("\n"); + } else { + printer_->Print("(all empty)\n"); + } + printer_->Undent(); + return true; + } + bool PrintTableType(const ResTable_type* chunk) { printer_->Print(StringPrintf(" id: 0x%02x", android::util::DeviceToHost32(chunk->id))); printer_->Print(StringPrintf( @@ -864,6 +941,10 @@ class ChunkPrinter { PrintTableType(reinterpret_cast<const ResTable_type*>(chunk)); break; + case RES_TABLE_TYPE_SPEC_TYPE: + PrintTypeSpec(reinterpret_cast<const ResTable_typeSpec*>(chunk)); + break; + default: printer_->Print("\n"); break; diff --git a/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt b/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt index 1abe77fd3ceb..f260e2733843 100644 --- a/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt +++ b/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt @@ -188,7 +188,7 @@ object SystemFeaturesGenerator { ?: throw IllegalArgumentException( "Invalid feature version input for $name: ${featureArgs[1]}" ) - FeatureInfo(name, featureArgs[1].toInt(), readonly = true) + FeatureInfo(name, featureVersion, readonly = true) } } } diff --git a/wifi/wifi.aconfig b/wifi/wifi.aconfig index 90d13e628ad6..369b80994e1b 100644 --- a/wifi/wifi.aconfig +++ b/wifi/wifi.aconfig @@ -38,6 +38,15 @@ flag { } flag { + name: "usd" + is_exported: true + namespace: "wifi" + description: "Unsynchronized Service Discovery" + bug: "340878198" + is_fixed_read_only: true +} + +flag { name: "hotspot_network_connecting_state_for_details_page" namespace: "wifi" description: "Update getConnectedState in HotspotNetworkEntry so that details page displays correctly." |